# How to broadcast a value over functions instead of a function over values?

This is a somewhat unusual usage, but I’m wondering if given

``````julia> M = [sin cos; cos sin]
2×2 Matrix{Function}:
sin  cos
cos  sin
``````

is there an easy way to broadcast the function evaluation over the elements for one specific argument? I want the result

``````julia> [x(pi/3) for x in M]
2×2 Matrix{Float64}:
0.866025  0.5
0.5       0.866025
``````

except using the broadcasting syntax instead of a comprehension. I thought that it’ll be something like

``````julia> getindex.(Ref(M), CartesianIndices(M)).(pi/3)
ERROR: MethodError: objects of type Matrix{Function} are not callable
Use square brackets [] for indexing an Array.
``````

but this doesn’t work.

Why not treat both `M` and `pi/3` like arguments for a separate method to broadcast?

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

julia> apply.([sin cos; cos sin], pi/3)
2×2 Matrix{Float64}:
0.866025  0.5
0.5       0.866025
``````
2 Likes

Marking this as the solution, as the following works and is quite concise:

``````julia> M = [sin cos; cos sin]
2×2 Matrix{Function}:
sin  cos
cos  sin

julia> map.(M, pi/3)
2×2 Matrix{Float64}:
0.866025  0.5
0.5       0.866025
``````
1 Like

Oh I never realized `map` returns a `Number` if given a `Number`, good to know! Bear in mind this doesn’t work for scalars in general, `map` falls back to expecting an iterable:

``````julia> map(sin, pi/3)
0.8660254037844386

julia> struct A end

julia> f(::A) = 1
f (generic function with 1 method)

julia> map(f, A())
ERROR: MethodError: no method matching length(::A)
``````

Presumably, iteration needs to be defined for the type for `map` to work correctly. The `apply` solution works as expected:

``````julia> M = [f f; f f];

julia> apply.(M, Ref(A()))
2×2 Matrix{Int64}:
1  1
1  1
``````

There is actually a `map` method specifically for `Number`s to get around iteration so it doesn’t need to allocate an output vector.

``````julia> struct Num2{T<:Number} t::T end # iterable contains 1 Number
julia> Base.iterate(x::Num2) = iterate(x.t)
julia> Base.iterate(x::Num2, y) = iterate(x.t, y)
julia> Base.length(x::Num2) = length(x.t)

julia> map(sin, Num2(pi/3))
1-element Vector{Float64}:
0.8660254037844386

julia> @which map(sin, Num2(pi/3))
map(f, A) in Base at abstractarray.jl:2896

julia> map(sin, pi/3)
0.8660254037844386

julia> @which map(sin, pi/3)
map(f, x::Number, ys::Number...) in Base at number.jl:272
``````
``````julia> π/3 .|> M
2×2 Matrix{Float64}:
0.866025  0.5
0.5       0.866025
``````
5 Likes