A most harrowing collection of Julia WATs

Hello all! :wave:
I’ve been collecting a few of the most cursed corner cases of Julia WATs that I know of here (WIP), but I would appreciate if people could share their own rare specimens as well.

Please ping me with your contributions and I’ll post with proper credit.

Here’s an example of what I mean by a Julia WAT:

julia> e = 9998.0
9998.0

julia> 2e
19996.0

julia> 2e+4
20000.0

julia> 2e+5 # This should be 20001.0, right?
200000.0

julia> 2e + 5 # note the spacing
20001.0

…wat.

Some of these are just unavoidable corner cases of multiple design constraints, others are an artifact of parsing being hard, but they’re all supposed to be tongue-in-cheek puzzles of our dear JuliaLang <3.

I mean, as a scientist I did not understand the point until the last line, because the 2e+4 immediately was parsed by my mind as a number in scientific notation and I did not understand the relation with the previous e or what you did mean by your first comment.

Surely there are some Julia WATs, but this seems kinda expected from any language with both floating points and implicit multiplication, is Julia the only language with these two characteristics?

Interestingly, syntax highlighting makes it hard to understand the wat

See also Bogumił Kamiński’s

Pitfalls of macro invocation in Julia

julia> macro m(args...)
           show(args)
       end
@m (macro with 1 method)

julia> @m 1 + 1
(:(1 + 1),)
julia> @m 1+1
(:(1 + 1),)
julia> @m 1 +1
(1, 1)

I always thought allowing things like 2x (instead of requiring 2*x) was just setting up things to fail in
unexpected ways. Wat!?

One of your WATs isn’t quite true:

However, the other example of “Shadowing” does reproduce the error shown that Kristopher Carlsson pointed out:
image

Probably because you already used - in your session

One from many on another of Bogumił Kamiński’s blog posts

julia> isequal(sum([-0.0, -0.0]), sum([-0.0, -0.0], init=0.0))
false

Ah, thanks @giordano, looks like you’re right:

Great collection! A couple of comments though:

  • “and, or on empty collections”: not spelled out, what is the WAT? Or is it really in reference to the “broadcasting shenanigans” below it?
  • “what is this syntax?” I’m not sure what’s wrong with it.
julia> f = function(x) x+1 end
#11 (generic function with 1 method)

julia> f(3)
4

It’s just a way to declare an anonymous function—IMO it’s an elegant syntax because it makes it very clear that what you’re doing is not assigning a name to the function.

That looks perfectly fine to me, no surprise if it behaves like arrays.

This is well documented in ?isequal

..
 julia> 0.0 == -0.0
 true

 julia> isequal(0.0, -0.0)
 false
..

Another one for the shadowing category

julia> Iterators = (map = (f, x)->f.(x).^2,)
(map = var"#1#2"(),)

julia> Iterators.map(identity, 1:5)
5-element Vector{Int64}:
  1
  4
  9
 16
 25

It’s a bit contrived, but actually happened to me in a different way because I accidentily pasted a compat requirement into the REPL…

For me it was that I thought that the anonymous function syntax was only valid using ->, so this came as a surprise.

Looking at it, I also like it too now :stuck_out_tongue_winking_eye:

julia> const a = (function(x)
    # more code
    x^2
end)(2)
4

julia> const b = ((x) -> begin
    # more code
    x^2
end)(2)
4

It is more readable for in-place functions (although I don’t know if people use them, maybe for some initializer).

Thanks for the comments @tim.holy !

Yes, those comments are basically TODOs that I pepper in my blogposts and as I come back to them I overwrite them - perhaps the big :construction: WIP :construction: signs should be a bit more prominent.

And although I agree with your comments on the anonymous function syntax - it definitely made me say “WAT? You can do that?” when I first saw it, so I’m guessing it’s gonna punk other people too on their first encounter.

Miguel

This code is inefficient when called with an integer argument:

function f(x)
    x += 1
    x /= 2
    return x
end

To make it efficient, use different names for the temporary variables:

function f(x)
    y = x+1
    z = y/2
    return z
end

You see the source of the inefficiency with @code_warntype.

-erik

Infers just fine on 1.7:

julia> function f(x)
           x += 1
           x /= 2
           return x
       end
f (generic function with 1 method)

julia> @code_typed f(1)
CodeInfo(
1 ─ %1 = Base.add_int(x@_2, 1)::Int64
│   %2 = Base.sitofp(Float64, %1)::Float64
│   %3 = Base.div_float(%2, 2.0)::Float64
└──      return %3
) => Float64

To explain: @code_warntype isn’t telling the whole truth here, since the optimizer can actually deal with this when lowering slots to phi nodes.

The floating point example feels expected for me (probably not to someone who is newer to writing code) but the rest of it is very interesting! Thank you for the linked post, its helpful as I get more acquainted with the Julia programming language.