The assignment-form syntax f(x) = 5 and the equivalent function ... end syntax both implicitly declare f a const name in the definition’s global scope. This also occurs for struct definitions. A const name means that reassignment is undefined behavior or an error. Conversely, a non-const name cannot be made into a const name afterward; this threw the error you saw. Note that adding methods to an existing function or type like f() = 0 or replacing a method f(x) = 7, is not reassigning f. You’d need actual assignment syntax f = ... to try that.
The compiler can do some great optimizations when accessing const names; they’re actually necessary to let function calls and type instantiations be optimized at all. For example, if you defined f = x->5 and g(x) = f(x), g cannot assume f will be the same function and cannot be optimized much. You can get around this with some higher order functions like g(foo::Function, x) = foo(x), but you probably prefer to access global names rather than pass every function and type through the arguments like g(f, x).
julia> f = x->5
#3 (generic function with 1 method)
julia> g(x) = 5
g (generic function with 1 method)
julia> supertypes(typeof(f))
(var"#3#4", Function, Any)
julia> supertypes(typeof(g))
(typeof(g), Function, Any)
Indeed they are both functions. The difference is, the name g is now assigned to the function, whereas f is just a variable which currently stores the address to the anonymous function “#3” (you couldn’t call it with #3(2)` though).
julia> g = 2
ERROR: invalid redefinition of constant Main.g
Stacktrace:
[1] top-level scope
@ REPL[16]:1
julia> f = 2
2
I hope this clears it up for you! You can also take a look at the docs about this.
On a side-note, this seems to induce performance issues: