How to "freeze" parameters to functional objects?

Hi, I am generating function objects depending on many parameters that must be computed through complicated equations. I am writing a for loop to compute many different collections of these parameters and finally generate a functional object for each set of parameters, but serves as a simple function that applies to a single argument. The set of parameters will be implicit. However, the following experiment seems to show that the parameters aren’t “frozen” to the function object:

julia> a=1
1

julia> myfun(x) = a*x;

julia> myfun(4)
4

julia> a=2;

julia> myfun(4)
8

julia> a=nothing

julia> myfun(4)
ERROR: MethodError: no method matching *(::Nothing, ::Int64)

In this case a is the parameter. I need it to be fixed rather than changing with the environment. What is the rule that governs the values of the parameters? How to make them fixed?

Your description of the problem is what “functors” are about:

julia> struct Object
           a::Float64
       end

julia> (o::Object)(x) = o.a*x 

julia> o = Object(4)
Object(4.0)

julia> o(5)
20.0

julia> b = Object(2)
Object(2.0)

julia> b(5)
10.0

but maybe if you describe your problem in more detail other better and simpler solutions may be what you want.

It is more common just to separate the “function” from its parameters, and pass the parameters explicitly to the function. If many of them, packed into a struct:

julia> f(x,parameters) = parameters.a*x + parameters.b*x
f (generic function with 1 method)

julia> struct Parameters
           a::Float64
           b::Float64
       end

julia> p = Parameters(5.0,10.0)
Parameters(5.0, 10.0)

julia> f(2.0,p)
30.0

2 Likes

A let block is also an easy way to introduce a new binding in your closure:

julia> myfun = let a = 1
         # the `let` block creates a new local variable named `a`, 
         # and the `x -> a * x`  is a function which captures that variable
         x -> a * x
       end
#5 (generic function with 1 method)

julia> myfun(2)
2

# Setting a global variable also named `a` doesn't change anything
# because `myfun` captured its very own local variable. The fact that they
# both happen to be named `a` doesn't matter at all. 
julia> a = 5
5

julia> myfun(2)
2
2 Likes

Higher order function approach:

julia> makemyfun(a) = x-> a*x
makemyfun (generic function with 1 method)

julia> myfun = makemyfun(2)
#3 (generic function with 1 method)

julia> myfun(4)
8
2 Likes