Redefinition of functions sometimes possible?

Hi!

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?

Yes. The former is a function named f. The latter is a pointer to an anonymous function. In a sense, the two are akin to

g(x) = x+1

f(x) = g(x) # the first one
f = g # the second
3 Likes

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
5 Likes

Thank you both!

One follow up questions:
Why can I change f(x) = x + 1 to f(x) = x + 2 when it is a constant variable? Are functions in some way mutable?

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
4 Likes

When you write

f(x) = x + 1

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.

4 Likes

What is displayed if you just write g after adding that method? (On my phone, can’t check)

julia> g
#4 (generic function with 1 method)
2 Likes