Trouble in understanding how to use varargs as a constructor

Hi, I have trouble understanding the bolded line following code from RecursiveArrayTools.jl.

struct ArrayPartition{T,S<:Tuple} <: AbstractVector{T}
x::S
end
## constructors
**ArrayPartition(x...) = ArrayPartition((x...,))**

function ArrayPartition(x::S, ::Type{Val{copy_x}}=Val{false}) where {S<:Tuple,copy_x}
  T = promote_type(eltype.(x)...)
  if copy_x
    return ArrayPartition{T,S}(copy.(x))
  else
    return ArrayPartition{T,S}(x)
  end
end

My understanding of the bolded line is that it allowed me to have as an input more than one object of type tuple e.g. ArrayPartition((1,2), (3,4)). What I don’t understand however is what is the function of the colon after x… on the RHS, ArrayPartition(x…) = ArrayPartition((x…,)). More specifically, I know that (x…,) is actually ((1,2),(3,4)) but I can understand how this syntax ArrayPartition((x…,)) produces such result.

x... is the splatting operator. (y,) just produces a 1-Tuple. Combined, they create an N-tuple containing the elements of x:

julia> x = [(1,2),(3,4)]
2-element Array{Tuple{Int64,Int64},1}:
 (1, 2)
 (3, 4)

julia> (x...,)
((1, 2), (3, 4))

Thank you, this is very clear. I was not aware of extra explanation of the splatting operation in the FAQ section.

After playing around a bit more with the splatting operator, I found out that

(x...,) 

is actually the same as

tuple(x...)

My question now is in what sense " … " are used. Splitting or Splatting?

x... used in a function type signature collects arguments into a tuple:

julia> f(args...) = typeof(args)
f (generic function with 1 method)

julia> f(1, 2, 3)
Tuple{Int64,Int64,Int64}

While x... in the context of a function call separates them out into individual args again. It does the opposite thing in opposite contexts.

1 Like

Thank you for the reply, but just to make sure that I understand what you are saying correctly. In the example above I have x = [(1,2), (3,4)]. In the case of Tuple(x…), x… is in the context of a function call. Hence, x… separates x into individual args which are (1,2) and (3,4). Thus Tuple(x…) is the same as Tuple((1,2),(3,4)). What is a function type signature (is it just typeof( ))?

In my OP, I also try to understand the meaning of

ArrayPartition(x…) = ArrayPartition((x…,))

My understanding now is that ArrayPartition(x…) is just a new constructor for ArrayPartition, and the meaning of x… in this case depends how the constructor is defined.

ArrayPartition(x…) = ArrayPartition((x…,))

This is confusing because the splatting and tuple constructor in ArrayPartition((x...,)) is redundant.

ArrayPartition(x) would do the same thing!

julia> x = (1,2,3)                                                                                                                             
(1, 2, 3)                                                                                                                                      
                                                                                                                                               
julia> y = (x...,)                                                                                                                             
(1, 2, 3)                                                                                                                                      

julia> x == y                                                                                                                                  
true                                                                                                                                           

So ArrayPartition is passed multiple arguments but it stores them all in a tuple in a single field.

Also your understanding of splat in a function call is correct.

by “in a function type signature” I meant when used on a function argument like f(x...) = x
sorry my descriptive terms still are a mess from Haskel and ruby and everything else, so my Julia has an accent…