On the arbitrariness of truth(iness)

If someone is feeling especially impish, they don’t even need to wait for this to change in the base language.

using IRTools
istruthy(b::Bool) = b
istruthy(x) = convert(Bool, x)
istruthy(s::String) = s != ""
istruthy(t::Tuple) = length(t) != 1

IRTools.@dynamo function truthy(f, args...)
    ir = IRTools.IR(f, args...)
    ir === nothing && return
    IRTools.recurse!(ir)

    for block in IRTools.blocks(ir)
        bblock = IRTools.BasicBlock(block)
        for b in eachindex(IRTools.branches(bblock))
            branch = bblock.branches[b]
            if IRTools.isconditional(branch)
                converted = push!(block, IRTools.xcall(@__MODULE__(), :istruthy, branch.condition))
                bblock.branches[b] = IRTools.Branch(branch; condition=converted)
            end
        end
    end
    return ir
end
julia> truthy() do
           if (1,)
               println("hi")
           end
       end
hi

julia> function bar(s::String)
           while s
               s = s[1:end-1]
               @show s
           end
           s
       end
bar (generic function with 2 methods)

julia> truthy() do
           bar("Hello")
       end
s = "Hell"
s = "Hel"
s = "He"
s = "H"
s = ""
""

Credit (or blame) for this monstrosity to Phipsgabler (not sure what his discourse handle is) sloppyif.jl · GitHub

14 Likes

I see it now, hahaha. It lacks a return, so all the “returns” are undefined which is falsy…

3 Likes

I recommend people who ask me the similar thing to explain to me the following Python code:

>>> all([])
True
>>> all([[]])
False
>>> all([[[]]])
True
37 Likes

I love the fact that Julia disallows non-Bool types in conditions, it saves a lot of bugs.

38 Likes

That, or remove the braces (but in my case I had a more complicated expression so removing braces wasn’t an option) because

arrow functions do not magically guess what or when you want to “return”

Returning the last expression would be a too reasonable option, I guess.

3 Likes

4 posts were split to a new topic: Assembly: is “jump if not zero” more expressive than “jump if odd”?

Fortranners (at least this one) agree with you. Fortran has a logical type, and if you want to convert an integer to a boolean you write

tf = merge(.true., .false., i /= 0)

All these fancy-schmancy notions of “correctness”, “consistency” and “error resistance” in Julia feel so 20th century to me. Those other languages have apparently embraced postmodern relativism, that truth can be whatever feels right to you depending on your viewpoint and daily mood. That approach is clearly more inline with the Zeitgeist, as evidenced in the news every day. It’s sad to see Julia so out of touch with our times. :wink:

24 Likes

jnz/jz are very useful for pointers.

if p = function_with_optional_result(...)
    # use p, as it is non-null
end

is a common pattern in, for example, the LLVM code base.

I’m not saying this is a better convention than explicit optional types (or returning nothing that’ll throw an error if you try to use it as a pointer), but it’s still fairly convenient.

Ptr types may not be integers, but it’s fairly reasonable to have them behave the same way, and thus have truthiness of integers also be based on comparison to zero.
I don’t know the history, but this also fits the pattern of C vs Lisp having settled on different conventions of truthiness for integers.

1 Like

A post was merged into an existing topic: Assembly: is “jump if not zero” more expressive than “jump if odd”?

In the first example, Julia does the same thing, which is common in logical systems.

julia> all([])
true

all([[]]) is false because the only value is an empty list, which is falsy.

In [4]: x = []

In [5]: bool(x)
Out[5]: False

In [6]: all([x])
Out[6]: False

all([[[]]]) is too complicated to read, but breaking it up we can see the only value is a nonempty list, so it is truthy.

In [1]: x = [[]]

In [2]: bool(x)
Out[2]: True

In [3]: all([x])
Out[3]: True
3 Likes

As much as I agree with the boolean/nonboolean distinction, I often wish Julia had gone one step further and not considered booleans to be integers.

julia> supertypes(Bool)
(Bool, Integer, Real, Number, Any)

julia> true + false
1
10 Likes

I will say that adding booleans is something I’ve done quite a few times before, basically counting the number of times something happened etc. I guess one could cast the bools to int to do that but it’s a bit verbose.

4 Likes

Stick that on a t-shirt and sell it at JuliaCon.

20 Likes

Stephen Colbert is somewhere sarcastically fuming right now

6 Likes

That’s not a step further, it’s a step in an orthogonal direction.

Booleans are integers anyways, at least to same extent as Int64 and Int8 are (modular integer rings). What is gained by pretending otherwise?

3 Likes
julia> all([[]])
ERROR: TypeError: non-boolean (Vector{Any}) used in boolean context
Stacktrace:

what do you mean…

Think the first example was the one referenced, i.e.

julia> all([])
true
1 Like

If they were modular integers like Int64, they would wrap with addition, no?

julia> typemax(Int) + 1
-9223372036854775808

julia> typemax(Bool) + true
2
3 Likes

that’s just Vacuous truth - Wikipedia which is very common. The problem with Python is [] can be interpreted as Boolean, which Julia is very much not able to

2 Likes