# Slurping in not final position

I was running the roames notebook exercises, in an attempt to fix this:

Create a more generic reduce2 function that works recursively over a tuple

For the operations + and *, being associative, simulate the operation in this direction 1*(2*(3*(4))) or on the contrary (((1)*2)*3)*4, the result is the same.
However, wanting to extend the result also to the operations (-) and (/) I tried to create the second structure, i.e. ((((1/2)/3)/4)/5) which is the one adopted by the library reduce function.
In the absence of slurping in a not final position, I had to use this somewhat convoluted method.

reduce3(f, t::Tuple)= _r(f,reverse(t)...)
_r(f,(x,))=x
_r(f,h,t...)=f(reduce3(f,reverse(t)),h)

Iâ€™ve tried using non-trailing slurping, which I thought was possible in version 1.9 of julia, also as function arguments as well as for assigning values to multiple variables.

With â€śnon-trailing slurpingâ€ť, are you referring to something like this:

f(a, b..., c, d) = # something

? If not, which syntax exactly are you referring to?

yes I tried to use this expression

_r(f,t...,h) =...

I take the opportunity to ask another question.
Why do the following two definitions seem equivalent?

julia> g((x,))=x^2
g (generic function with 1 method)

julia> g(2)
4

julia> g(x)=x^2
g (generic function with 1 method)

julia> g(2)
4

If you allow slurping in non-trailing position, the following two method definitions would always be ambiguous:

f(a, b, c..., d) = # something
f(a, b..., c, d) = # something

There are lots of possible combinations to get such an ambiguity, so I think thatâ€™s why itâ€™s likely not supported.

Because g((x,)) = ... is the same as g(x,) = ..., which is the same as g(x) = ....

2 Likes

Thank you!
On the main question (that of slurping) I have to think about it calmly.
For the secondary question: why donâ€™t the same parsing rules hold for the case of a tuple with two elements?

julia> g((x,y))=x+y
g (generic function with 1 method)

julia> g(x,y)=x+y
g (generic function with 2 methods)

No, itâ€™s not the same. The first one does tuple destructuring, the last one doesnâ€™t.

julia> f1((x,)) = x^2
f1 (generic function with 1 method)

julia> f2(x) = x^2
f2 (generic function with 1 method)

julia> f1((2, 3))
4

julia> f2((2, 3))
ERROR: MethodError: no method matching ^(::Tuple{Int64, Int64}, ::Int64)
2 Likes

So the two expressions (two methods!??!, why only one method then?)) are equivalent only for input of a single element?

julia> g((x,))=x.^2
g (generic function with 1 method)

julia>

julia> g(((2,3),))
(4, 9)

julia> g(3)
9

julia> g(2,3)
ERROR: MethodError: no method matching g(::Int64, ::Int64)

julia> g(x)=x.^2
g (generic function with 1 method)

Both g(x) = ... and g((x,)) = ... define g(::Any), so the one defined last overwrites the first one.

Thatâ€™s a side effect of numbers being iterable, so you can destructure a single scalar.

julia> f((x,)) = x
f (generic function with 1 method)

julia> f(1)
1

julia> f("foo")
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)

julia> struct Foo end

julia> f(Foo())
ERROR: MethodError: no method matching iterate(::Foo)
2 Likes

I donâ€™t want to abuse your availability, but I also try to ask the reason for the following results:

julia> t,h...=1
1

julia> t
1

julia> h
Base.Iterators.Rest{Int64, Nothing}(1, nothing)

julia> t...,h=1
1

julia> t
Int64[]

julia> h
1

julia> k,t...,h = 1
ERROR: ArgumentError: The iterator only contains 0 elements, but at least 1 were requested.

This assigns the first element to t, and the rest to h. In this case, h is empty, as there is only one element on the right-hand side.

In this case, the last element is assigned to h, and the elements before that are assigned to t. Since there is only one element on the right, t is empty.

In this case, the first element is assigned to k, the last to h, and the ones in between to t. However, as there is only one element on the right-hand side, the operation leads to an error. This assignment expects at least two values on the right.

julia> k, t..., h = 1,2
(1, 2)

julia> t
()

julia> k
1

julia> h
2
2 Likes

Okay. I understand. Although Iâ€™m still curious as to why t... is Int[] in one case, Base.Iterators.Rest{Int64, Nothing}(1, nothing) in another, and yet another ().

In the meantime I have found, by trial and error, a way to simulate slurping even in non-final position

julia> reduce4(f, t::Tuple)= _r(f,t...)
reduce4 (generic function with 1 method)

julia> _r(f,x)=x
_r (generic function with 1 method)

julia> _r(f,(t...,h)...)=f(reduce4(f,t),h)
_r (generic function with 2 methods)

julia> reduce4(*, (1,2,3,4,5))
120

julia> reduce4(+, (1,2,3,4,5))
15

julia> reduce4(-, (1,2,3,4,5))
-13

julia> reduce4(/, (1,2,3,4,5))
0.008333333333333333

!