# Combining functors, broadcasting, and pipes

**URL:** https://discourse.julialang.org/t/combining-functors-broadcasting-and-pipes/48691
**Category:** General Usage
**Tags:** broadcasting, piping, functors
**Created:** [October 20, 2020, 2:45pm UTC](https://discourse.julialang.org/t/combining-functors-broadcasting-and-pipes/48691 "2020-10-20T14:45:26Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![klaff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klaff/32/7637_2.png) [@klaff](https://discourse.julialang.org/u/klaff)
#### Post date: [October 20, 2020, 2:45pm UTC](https://discourse.julialang.org/t/combining-functors-broadcasting-and-pipes/48691/1 "2020-10-20T14:45:26Z")

</div>

I’ve got another behavior I don’t quite understand. Here’s an MWE:

```julia
begin
	struct MyStruct
		x
	end
	function(ms::MyStruct)(y)
		ms.x*y
	end
end

ms1=MyStruct(2)

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

```julia
ray1 |> distance1 |> lens1 |> distance2

```

which worked great for a single ray, but broke down when I tried to do

```julia
@. rays |> distance1 |> lens1 |> distance2

```

Thanks!

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [October 20, 2020, 3:00pm UTC](https://discourse.julialang.org/t/combining-functors-broadcasting-and-pipes/48691/2 "2020-10-20T15:00:47Z")

</div>

I think the infix syntax is a bit of a distraction here. If you define a new function:

```julia
julia> function pipe(x, f)
         x |> f
       end
pipe (generic function with 1 method)

```

then I hope you’ll agree that:

```julia
(3, 4) .|> ms1

```

should be equivalent to:

```julia
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.

Wrapping `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:

```julia
Base.broadcastable(s::MyStruct) = Ref(s)

```

at which point all of your examples should now work as intended:

```julia
julia> (3,4) .|> ms1
(6, 8)

```

---

<div class="post-metadata">

### Author: ![klaff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klaff/32/7637_2.png) [@klaff](https://discourse.julialang.org/u/klaff)
#### Post date: [October 20, 2020, 4:27pm UTC](https://discourse.julialang.org/t/combining-functors-broadcasting-and-pipes/48691/3 "2020-10-20T16:27:12Z")

</div>

> [@rdeits](#):
>
> `Base.broadcastable(s::MyStruct) = Ref(s)`

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](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.
