Define a function in global scope inside another function

I want to define a function a(x) inside a function b(a), but make a in global scope, example:

a(x) = 3 + x
function b(a)
    global a(x) = a + x
end

This gives an error currently:

ERROR: syntax: Global method definition around REPL[14]:2 needs to be placed at the top level, or use "eval".
julia> a(x) = 3 + x
a (generic function with 1 method)

julia> a(2)
5

julia> function b(a)
           @eval a(x) = $a + x
           return nothing
       end
b (generic function with 1 method)

julia> b(10)

julia> a(2)
12

(But likely a bad idea in the first place :wink:)

1 Like

Likely this is not actually something you want to do, so it would be good if you described the problem you are trying to solve so we avoid X/Y problems.

Otherwise its something we often use a macro for.

2 Likes

My problem is this: Initialize some functions at every remake in PDEProblem

I’ve thought a possible solution would be to redefine

Temperature(t,x)

by defining a function:

function pde_solution(Tvec)
    interp = LinearInterpolation(Tvec, [1.,2.,3.,4.,5.])
    global Temperature(t, x) = interp(x)
    solve(prob, TRBDF2())
end

This is generally not a good idea and won’t work out of the box because the already compiled code won’t see the redefined method until you hit global scope. You’d need to use Base.invokelatest inside the solve which is not performant. See documentation.

You could try is pass that function as another parameter into the problem or define some sort of struct where you can then change the values later on.

1 Like

Ok, no don’t do this :wink:

globals are very difficult to reason about, you are opening yourself up to a lot of potential bugs, and poor performance too.

I don’t totally understand your use case as your code is not a full MWE (e.g. what is prob). But you could maybe use a functor ? that’s a struct that can be called like a function:

struct Temperature{L}
    interp::L
end
(f::Temperature(t, x) = f.interp(x) # But why don't you use `t` ??
interp = Temperature(LinearInterpolation(Tvec, [1.,2.,3.,4.,5.]))

There are two alternative approaches that you can use while avoiding the global @eval call.

The best fit depends on your use case:

A) Your goal is to update/redefine the logic (body) of an existing globally defined function

Take a look at DynamicExpressions.jl package and check if your function logic can be defined this way.

The idea is that you could have a Ref inside your original global function body and only update that piece using DynamicExpressions.jl.

B) You are generating multiple functions and want to be able to call them from the global scope.

This scenario is not excluding the usage of DynamicExpressions.jl - you might still find the approach feasible.

The alternative is to generate anonymous functions and update a global dictionary:

const FUNSTORE = Dict{Symbol,Function}()

# from inside some function
FUNSTORE[:myfun] = x -> x + 1
FUNSTORE[:myfun](1) # will produce 2