I’ve got another behavior I don’t quite understand. Here’s an MWE:
ms1(2) # works
2 |> ms1 # works
ms1.((3,4)) # works
broadcast(ms1, (3,4)) # works
(3,4) .|> ms1 # does not work (throws MethodError: no method matching length(::MyStruct) ...
(3,4) .|> (ms1,) # works
(3,4) .|> x->ms1(x) # [EDIT: this works too]
I don’t understand why the
.|> approach requires that
ms1 be wrapped in a tuple as if it were an array, tuple, or other iterable.
P.S. This came about because I’m playing with ray transfer matrices and paraxial raytracing and tried to build a construct like
ray1 |> distance1 |> lens1 |> distance2
which worked great for a single ray, but broke down when I tried to do
@. rays |> distance1 |> lens1 |> distance2
I think the infix syntax is a bit of a distraction here. If you define a new function:
julia> function pipe(x, f)
x |> f
pipe (generic function with 1 method)
then I hope you’ll agree that:
(3, 4) .|> ms1
should be equivalent to:
pipe.((3, 4), ms1)
When written without the
|> infix, I think it becomes more obvious why this doesn’t work: you are broadcasting over both the input
(3, 4) and your struct
ms1 itself. Julia doesn’t know how to iterate over a
MyStruct, so this broadcast fails.
ms1 in a 1-element tuple fixes the issue because you are now broadcasting over that tuple, which works just fine.
Ref(ms1) would also work, and is a bit more idiomatic in Julia.
You can tell Julia to treat your struct as a scalar in broadcasting operations with:
Base.broadcastable(s::MyStruct) = Ref(s)
at which point all of your examples should now work as intended:
julia> (3,4) .|> ms1
Thank you, that absolutely works and is what I needed.
EDIT: Also, thanks for the explanation. After a bit of reflection, I think some of my confusion came from thinking, in the context of pipes, of the functor as a function. Functions are not broadcasted over (they are protected by Base.broadcastable in the same way you advised I do for my struct (see https://github.com/JuliaLang/julia/blob/539f3ce943f59dec8aff3f2238b083f1b27f41e5/base/broadcast.jl#L678), but a functor is not a function (specifically
isa(ms1, Function) == false) even though it has a method.