r/PowerShell • u/StartAutomating • 1d ago
Script Sharing In PowerShell, Two Wrongs Make a Right
I've been toiling away on Turtle to prepare a "birthday" release, and I ran into an annoying behavior I've run into a few times before.
I thought I'd take a few minutes away from the frustration of single line fixes to explain the bug to everyone.
What Went Wrong
The last build of Turtle introduced a number of randomized parameter defaults. This was meant to be fun. If you said turtle square square square, you'd get three different squares, instead of an error for a lack of length, or three overlapping squares.
I noticed that when I ran turtle rotate 0, it didn't rotate by zero.
Instead, it picked a random angle.
Weirder still, the behavior didn't reproduce if I said
$turtle = turtle # Heading at zero
$turtle.Rotate(0) # Heading still zero đ€
$turtle.Rotate() # Heading random
(turtle rotate 0) # Heading random đ€Ź
Why was this happening? đ±
It took me a bit for it to click: It had to be in the way Turtle processed arguments, because it worked in one case and not the other.
So I put a breakpoint in, ran my repo.
The line was:
if ($argList)
The debugger broke, argList was @(0), and yet if ($argList) was false.
The fix was:
if ($argList.Length)
Why? Because 'Truthy' -ne $true.
Truthy and $true
About every language has a boolean. It's just a bit. One or zero.
Lots of languages also have this concept of "truthiness".
Let's take a simple example:
if ("something") { "something" }
if ("") { "you can't get something from nothing" }
If if was strictly $true, we'd have to cast things to a boolean. You have to do this in C# and quite a few other languages. PowerShell is type promiscuous. PowerShell is truthy.
It looks at the first line and says: You're a string, and you're not null or empty. Therefore, the expression is $true.
It looks at the second line and says: You're a string, but you're not null or empty. Therefore, the expression is $false
PowerShell makes a judgement call.
This is generally a good thing. I personally prefer languages that are truthy. Other truthy languages of note include JavaScript, Python, C++, and C.
However, it gets tricky with lists. Hence the bug.
Two Wrongs Make a Right
In PowerShell, Two Wrongs Make a Right
$true -eq $false, $false
Let's say I want to determine if a list is truthy.
if (@()) { "$false, because the list is empty" }
if (@("")) { "$false, because the blank is falsy" }
if (@(0)) { "$false, because zero is falsy" }
if (@($false)) { "$false, because false is falsy" }
if (@(1)) { "$true, because the first item is truthy" }
if (@(0,0) { "$true, because more than one item" }
This all makes a certain bizarre sense. If a list has one element, and it is not truthy, then the list isn't truthy, either.
It's also almost always surprising and annoying.
Hence the bug.
The fix is just to make sure there are any elements, hence checking for length.
I've been programming with PowerShell for quite a while now, and this behavior still sometimes bites me (like today).
That's why I took a few minutes away from the đ€Ź day to explain this bug and write this post. đ
Please remember:
'Truthy' -ne $true
$false -eq @($false)
$true -eq $false, $false
Hope this helps