Is there a way to deepcopy a function in julia?

Viewed 206

Pretty much what the title says. If I assign g=f or do g=deepcopy(f), the result is the same: redefining f will redefine g too. Is there a way to make g an independent copy of f?

julia> function f(x) x end
f (generic function with 1 method)

julia> f(1)
1

julia> g = deepcopy(f)
f (generic function with 1 method)

julia> g(1)
1

julia> function f(x) x+1 end
f (generic function with 1 method)

julia> f(1)
2

julia> g(1)
2
1 Answers

AFAICT functions in Julia are considered to be bits type:

julia> f(x) = x
f (generic function with 1 method)

julia> isbitstype(typeof(f))
true

which means that deepcopy on them is a no-op. Here is a definition of deepcopy:

function deepcopy(x)
    isbitstype(typeof(x)) && return x
    return deepcopy_internal(x, IdDict())::typeof(x)
end

The reason for this is that each function has its own type (but it can have many methods).

The solution that phipsgabler proposes works because each time you define an anonymous function it gets its own new type. See here:

julia> typeof(x -> x)
var"#1#2"

julia> typeof(x -> x)
var"#3#4"

julia> typeof(x -> x)
var"#5#6"

This has one downside though - each time you pass an fresh anonymous function to another function it has to be compiled, e.g.:

julia> @time map(x -> x, 1:2);
  0.024220 seconds (49.61 k allocations: 2.734 MiB)

julia> @time map(x -> x, 1:2);
  0.023754 seconds (48.25 k allocations: 2.530 MiB)

julia> @time map(x -> x, 1:2);
  0.023336 seconds (48.25 k allocations: 2.530 MiB)

vs

julia> fun = x -> x
#7 (generic function with 1 method)

julia> @time map(fun, 1:2);
  0.023459 seconds (48.23 k allocations: 2.530 MiB)

julia> @time map(fun, 1:2);
  0.000016 seconds (4 allocations: 192 bytes)

julia> @time map(fun, 1:2);
  0.000013 seconds (4 allocations: 192 bytes)

Going back to your original question. Even if you write something like:

julia> f(x) = x
f (generic function with 1 method)

julia> g = x -> f(x)
#1 (generic function with 1 method)

julia> g(1)
1

you get:

julia> f(x) = 2x
f (generic function with 1 method)

julia> g(1)
2

because f(x) = 2x replaces the method for the function f but its type does not change (as commented above - one function can have many methods and you can even update the methods of the function as in the example above). This is related to a so called "world age" issue that you have explained here in the Julia manual.

Related