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.

27 Likes

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?

16 Likes

Interestingly, syntax highlighting makes it hard to understand the wat

13 Likes

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)
8 Likes

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

15 Likes

One of your WATs isn’t quite true:

1 Like

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

8 Likes

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
7 Likes

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

1 Like

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.

9 Likes

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
..
4 Likes

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…

2 Likes

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).

2 Likes

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.

1 Like

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

4 Likes

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.

12 Likes

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.

1 Like