7

I have this lambda style function call

foldr((l,r) -> r+1, "12345"; init=0)

which Julia happily computes as 5. Rewriting it to the do style looks like

foldr("12345"; init=0) do (l,r)   # I also tried ((l,r),) and just tup
    # l,r = tup   # and then destructure here
    r+1
end

As far as I understood, these two should be equivalent. Unfortunately, Julia 1.7.0-rc3 doesn't agree with me:

ERROR: MethodError: no method matching (::var"#36#37")(::Char, ::Int64)
Closest candidates are:
(::var"#36#37")(::Any) at REPL[25]:2
Stacktrace:
[1] FlipArgs
@ ./reduce.jl:196 [inlined]
[2] BottomRF
@ ./reduce.jl:81 [inlined]
[3] _foldl_impl(op::Base.BottomRF{Base.FlipArgs{var"#36#37"}}, init::Int64, itr::Base.Iterators.Reverse{String})
@ Base ./reduce.jl:58
[4] foldl_impl(op::Base.BottomRF{Base.FlipArgs{var"#36#37"}}, nt::Int64, itr::Base.Iterators.Reverse{String})
@ Base ./reduce.jl:48
[5] mapfoldr_impl(f::Function, op::Function, nt::Int64, itr::String)
@ Base ./reduce.jl:186
[6] #mapfoldr#246
@ ./reduce.jl:205 [inlined]
[7] #foldr#247
@ ./reduce.jl:224 [inlined]
[8] top-level scope
@ REPL[25]:1

Why does the first form work but the second not? Can it be made to work?

primfaktor
  • 2,831
  • 25
  • 34

1 Answers1

4

Parentheses are not needed here:

julia> foldr("12345"; init=0) do l, r
           r+1
       end
5

if you write (l, r) after do it means you are passing one argument that has two elements that should be assigned to l and r respectively, e.g.:

julia> foreach([1:2, 3:4, 5:6]) do (x,y)
       @show x
       @show y
       end
x = 1
y = 2
x = 3
y = 4
x = 5
y = 6

See the Julia Manual for an additional explanation of this rule.

Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
  • This doesn't only happen with do blocks, either, you could define a function as `f( (x, y) ) = x+y`; it only takes 1 argument and tries to iterate it for 2 elements to assign to x and y. If you annotate its argument type for dispatch, you'll have to do this `f( (x, y)::UnitRange ) = x-y`. Multiple assignment like `x, y = someIterable` is called destructuring, and method argument destructuring is specifically documented in depth here: https://docs.julialang.org/en/v1/manual/functions/#Argument-destructuring – BatWannaBe Dec 16 '21 at 07:33
  • Oh my, of course! I feel stupid. Wasn't there an older version of Julia where you needed to do it like this? – primfaktor Dec 17 '21 at 06:54
  • 1
    I have just checked the Julia 0.3 documentation and it uses `do a, b`, so it seems that it was always the recommended syntax (but indeed maybe in older Julia versions if you put parentheses around `a, b` it worked since tuple destruction in method signatures was not supported then) – Bogumił Kamiński Dec 17 '21 at 08:50