Missed the WAT thread, so here's one for intentionally cursed code

continues in a 1-iteration loop acting like @gotos to 1 @label because break wasn’t bad enough

function foo(A)
    for _ in 1
        if is1(A) continue end # @goto wrapup
        bar1!(A)
        if is2(A) continue end # @goto wrapup
        bar2!(A)
    end # @label wrapup
    baz(A)
end
3 Likes

I can’t really compete with this, but I just spent 20 minutes chasing down a bug which was essentially a

i -= 1

misspelled as a

i =- 1

within a branch that was rarely invoked.

2 Likes

How does Julia parsing work here?
Extending your example:

julia> + - + 1
-1
1 Like

That’s easy to discover.

julia> Meta.parse("i =- 1")
:(i = -1)
7 Likes

Unary +/- has high precedence, so it binds to the argument in a nested way. Specifically,

julia> Meta.parse("+ - + 1")
:(+(-(+1)))

Note that I am not blaming Julia for my =- mixup, I think it is perfectly OK to parse that. One cannot expect the parser to catch all mistakes, I should have been writing unit tests for seemingly simple building blocks too.

4 Likes

Just playing around I found out one can do:

f(x) = x -> + - +(x)

g(x) = f(x)(x)

g(1)  = -1

Which is quite weird, but neat to know.

2 Likes

This should arguably be a parser warning

1 Like
julia> struct X
           return 1
       end
1

julia> X()
ERROR: MethodError: no method matching X()

julia> foo(x::T) where {T <: return 1} = nothing
1

julia> foo(1)
ERROR: MethodError: no method matching foo(::Int64)
6 Likes

This is cursed. IDK what’s going on right now. Maybe it’s an undefined behavior or some weird interactions between language semantics.

julia> where where where where where where where
Any
7 Likes

you can have much more fun than that.

julia> why where when where what where who where why
Any
15 Likes

This is known and intentional, see

but I am not sure it is mentioned in the manual.

1 Like

To clarify, this occurs without the struct X return 1 end too, and it doesn’t make a method according to methods(foo). As far as I can tell from Meta.@lower, it’s the same behavior as early returns from a much less cursed let block, and early returns are treated the same as any other code in local scopes.

Please explain this one?

This basically reduces to

julia> A where B where A
Any

which I don’t fully understand why it doesn’t complain about B not being defined, but from there it’s just tacking on extra clauses.

The right hand side of the where introduces a new binding. You don’t necessarily need to use it:

julia> Int where asdf
Int64

This is introducing lots of unused where bindings. The only thing that needs to be defined — either by an existing binding or one of the newly introduced ones with the wheres — is the leftmost one.

8 Likes

Some more REPL clarification:

julia> A where A
Any

julia> Meta.@lower A where A
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Core.TypeVar(:A)
│        A = %1
│   %3 = Core.UnionAll(A, A)
└──      return %3
))))

I’m pretty sure that’s a nothingburger because involving curly braces makes apply_type throw an error if a parameter tries to be the parametric type:

julia> A{Int} where A
ERROR: TypeError: in Type{...} expression, expected UnionAll, got a value of type TypeVar
Stacktrace:
 [1] top-level scope
   @ REPL[63]:1

julia> Meta.@lower A{Int} where A
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Core.TypeVar(:A)
│        A = %1
│   %3 = A
│   %4 = Core.apply_type(A, Int)
│   %5 = Core.UnionAll(%3, %4)
└──      return %5
))))

As for the sequence of wheres, some of them are not the keyword where, just variables:

julia> where where where
Any

julia> Meta.@lower where where where
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Core.TypeVar(:where)
│        where = %1
│   %3 = Core.UnionAll(where, where)
└──      return %3
))))

Cursed code straight from the manual:

sqrt_second(x) = try
    sqrt(x[2])
catch y
    if isa(y, DomainError)
        sqrt(complex(x[2], 0))
    elseif isa(y, BoundsError)
        sqrt(x)
    end
end

sqrt_second([1,"2",3])  # no problemo!

The way error handling works in julia and that you have to explicitly re-throw errors makes me nervous and angry every time I have to do it.

I’m not attempting to criticize your post just understand it — isn’t that the whole point of a catch block? why would it rethrow automatically

Python has switch-like error handling where you can do

try:
    # some error code
except ExpectedErrorType:
    # handle that exception
except AnotherErrorType:
    # another way to handle error

If the error is not one of ExpectedErrorType or AnotherErrorType, Python falls back to rethrowing the error. It is considered bad practice in Python to do except: and generically handle all exceptions.

Coming from Python, I had similar friction with Julia’s try-catch syntax.

1 Like