Use of `Ref` in broadcasting user-defined functions

Hello,

I would like to apply a function that I’ve created to an array/range. Let me use a simplified version of my code to illustrate my question. It starts defining a structure for parameters, something like:

using Parameters

@with_kw struct myParams
   α::Float64 = 0.5
   β::Float64 = 0.5
end

p = myParams()

Then I create a function, like:

function myf(x::Float64, p::myParams)::Float64
    @unpack α, β = p

    return β*x^α
end

Now I want to apply this function, with a particular instance of myParams, to a range or array. I could do it using a for loop, but that seems not very julian. Using broadcasting, I thought of writing this:

@time myf.(range(0.0, stop = 100.0), p)

It gives an error:

MethodError: no method matching length(::myParams)
Closest candidates are:
  length(!Matched::DataStructures.IntSet) at /home/arnau/.julia/packages/DataStructures/ixwFs/src/int_set.jl:191
  length(!Matched::DataStructures.DiBitVector) at /home/arnau/.julia/packages/DataStructures/ixwFs/src/dibit_vector.jl:40
  length(!Matched::ExponentialBackOff) at error.jl:259
  ...
_similar_for(::UnitRange{Int64}, ::Type{Any}, ::myParams, ::Base.HasLength) at array.jl:597
_collect(::UnitRange{Int64}, ::myParams, ::Base.HasEltype, ::Base.HasLength) at array.jl:630
collect(::myParams) at array.jl:624
broadcastable(::myParams) at broadcast.jl:682
broadcasted(::Function, ::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, ::myParams) at broadcast.jl:1261
top-level scope at timing.jl:174 [inlined]
top-level scope at Tests.jl:0

I’ve tried this, and it works:

@time myf.(range(0.0, stop = 100.0), Ref(p))

However, after reading the documentation for Ref, I’m not sure I’m doing the right thing here. Any help?

Thank you.

You are using Ref in broadcasting correctly. Ref inhibits broadcasting from iterating over the fields of p.

If you want your type to always act as a scalar, you can tell the broadcasting machinery to do that:

Broadcast.broadcastable(mp::MyParams) = Ref(mp)

Then myf.(range, p) should work.

BTW, it’s a strong convention in Julia to make type names UpperCamelCase. So for clarity, you should probably call your type MyParams instead of myParams.

4 Likes