I noticed some unexpected (at least for me) behavior when redefining functions.
julia> foo(x) = x + 1
foo (generic function with 1 method)
julia> foo(x) = x + 2
foo (generic function with 1 method)
julia> foo = x -> x + 1
ERROR: invalid redefinition of constant foo
Stacktrace:
[1] top-level scope
@ REPL[3]:100:
and
julia> bar = x -> x + 1
#3 (generic function with 1 method)
julia> bar = x -> x + 2
#5 (generic function with 1 method)
julia> bar(x) = x + 1
ERROR: cannot define function bar; it already has a value
Stacktrace:
[1] top-level scope
@ none:0
[2] top-level scope
@ REPL[6]:1
Can someone explain why this happens? Are f(x) = x + 1 and f = x -> x + 1 fundamentally different?
In particular the most important difference for this question is that f = x -> x + 1 creates a global variable biniding, whereas f(x) = x + 1 creates a global constant binding.
If you make is a constant then you also can’t change it:
julia> const g = x -> x + 1
#4 (generic function with 1 method)
julia> const g = x -> x + 2
ERROR: invalid redefinition of constant g
Stacktrace:
[1] top-level scope
@ REPL[2]:1
f(x) = x + 1 creates a constant binding from the variable f to a function. However, the function itself is a mutable bag of methods. What you did was actually overwrite one of it’s methods. We can do this same with your other example:
julia> const g = x -> x + 1
#4 (generic function with 1 method)
julia> (::typeof(g))(x) = x + 2
julia> g(1)
3
you can think of this as ‘special syntax’ for something more like
struct var"typeof(f)" end
const f = var"typeof(f)"()
(::typeof(f))(x) = x + 1
Here’s that in action:
julia> struct var"typeof(f)" end
julia> const f = var"typeof(f)"()
var"typeof(f)"()
julia> (::typeof(f))(x) = x + 1
julia> f(1)
2
In julia, methods can be attached to any object. Functions are just a type of singleton object that have special syntax to make it easier to add methods to them.