# 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

``````

!