Julia function returning anonymous function - julia

I am trying to understand a relatively simple piece of code, but I am not quite able to reason what is happening (I am a Julia newbie coming from Python/Matlab background).
function myfunc(number::Integer)
double() = 2*number
square() = number^2
return _ -> (number, double, square)
end
I understand myfunc is returning an anonymous function that does not care for the value passed to it. So these cases make sense to me:
julia> n4 = myfunc(4)
#9 (generic function with 1 method)
julia> n4(50)
(4, var"#double#10"{Int64}(4), var"#square#11"{Int64}(4))
In the first line n4 refers to the anonymous function itself, whereas in the second the anonymous function is called with parameter 50 and does what it is supposed to: discards 50 and returns the tuple containing data it was defined with.
What I don't understand is how I am able to do:
julia> n4.square
(::var"#square#11"{Int64}) (generic function with 1 method)
julia> n4.square()
16
The fact that n4 which refers to an anonymous function has child objects n4.number, n4.double, n4.square is a surprise to me. How is n4 behaving as if it were a struct? Doing n4(*)[2]() to get back 8 as answer makes sense but when fieldnames(n4) fails something is happening behind the scenes that I don't understand to make n4.double() work. Where/what is the mechanism by which I am able to use . after n4 to get at the functions/data?

In Julia, all generic functions (that is, all regular Julia-defined functions) are structs. Any struct can be made callable in Julia, so by default, a normal function is just a zero-field struct (a singleton) made callable. In other words, doing
foo(x) = x+1
is similar to
struct Foo end
const foo = Foo()
(::Foo)(x) = x + 1
This works great for normal functions, but for anonymous functions, this can cause the creation of a large number of new types. For example, you could do:
functions = [x -> x+i for i in 1:1000].
Instead of creating 1000 new types, each with a new value of i, Julia here creates a single type containing i as a field, and 1000 instances.
In your case, instead of myfunc returning a new type for every invokation and returning the singleton instance of that type, it returns an instance of a type with the fields number, double and square.
Also, you can totally call fieldnames, you just have to call it on a type: fieldnames(typeof(myfunc(4))).
It's just an optimization, and it feels a little strange that you have code relying on the internals of how functions are represented in memory. You probably shouldn't do that.

Related

Why do loops in Julia introduce their own scope

As a novice to Julia, I, like many others, am perplexed by the fact that loops in Julia create their own local scope (but not on the REPL nor within functions). There is much discussion online about this topic, but most of the questions here are about the particulars of this behaviour, such as why doing a=1 inside the loop doesn't affect variable a outside the loop, but a[1]=1 works. I get how it works now, for the most part.
My question is why was Julia implemented with this behaviour. Is there a benefit to this from the prespective of the user? I cannot think of one. Or was it necessary for some technical reason?
I appologise if this has been asked already, but all the questions and answers I've seen so far were about how this works and how to deal with it, but I am curious about WHY Julia was implemented this way.
Firstly, loops in Julia only introduce a new scope of the sort that hides variables existing outside the loop (as per your complaint) if the scope outside the loop is global scope. So, for instance
function foo()
a = 0
# Loop does not hide existing variable `a`, will work just fine
for i = 100
a += i^2
end
return a
end
julia> foo()
10000
in other words
# Anywhere other than global scope
a = 0
for i = 100
a += i^2
end
a == 10000 # TRUE
This is because in Julia, as in many many other languages, global scope may be considered harmful. At the very least, a for loop with global scope would encounter significant performance penalties. For instance, consider the following:
julia> a = 0
0
julia> #time for i=1:100
# Technically this "global" keyword is superfluous since we're running this at the repl, but doesn't hurt to be explicit
global a += rand()^2
end
0.000022 seconds (200 allocations: 3.125 KiB)
julia> function bar()
a = 0
for i=1:100
a += rand()^2
end
return a
end
bar (generic function with 1 method)
julia> #time bar()
0.000002 seconds
33.21364180865362
Note the massive difference in allocations (the bottom version has zero) and the ~10x time difference.
Now, you may have noticed I used a special keyword global there in the global example, but since this was being run in the REPL, that doesn't actually do anything other than make it explicit what is happening.
That brings us to the other significant difference you have noticed: when run in the REPL, for loops appear not to introduce a new scope, even though the REPL is certainly global scope. This is because it turns out to be a huge pain when debugging to have to add a bunch of global qualifiers to code you have copy-pasted from somewhere deeper in your program (say within a function, where loops do not hide outside variables). So for the sake of convenience when debugging, the REPL effectively adds those global keywords for you, making the presumption that if you cared about performance you wouldn't just be pasting raw loops into the REPL, and if you are just pasting raw loops into the REPL, you're probably debugging or something.
In a script, however, it is presumed that you do care about performance, so you will get an error if you try to use a global variable within a loop without explicitly declaring it as such.
The details are substantially more complicated, as the other answer explains in more technically correct terms. Some of this complication, as far as I know, is due to a reversal on the decision of whether or not global variables should or should not be accessible by default within a loop in the REPL that happened around the time of Julia v0.7.
for loops in Julia introduce a so called local (soft) scope, see https://docs.julialang.org/en/v1/manual/variables-and-scoping/#man-scope-table.
The rules for local (soft) scope are (quoting):
If x is not already a local variable and all of the scope constructs containing the assignment are soft scopes (loops, try/catch blocks, or struct blocks), the behavior depends on whether the global variable x is defined:
if global x is undefined, a new local named x is created in the scope of the assignment;
if global x is defined, the assignment is considered ambiguous:
in non-interactive contexts (files, eval), an ambiguity warning is printed and a new local is created;
in interactive contexts (REPL, notebooks), the global variable x is assigned.
So your statement:
why doing a=1 inside the loop doesn't affect variable a outside the loop
is only true in non-interactive contexts if the for loop is not inside a hard local scope (typically if for loop is in a global scope), and the variable you assign to is defined in global scope. However, you will get a warning then.
Now the crucial part of your question is I think:
My question is why was Julia implemented with this behaviour. Is there a benefit to this from the prespective of the user?
The answer is that for loop creates a new binding for a variable that is defined within its scope. To see the consequence consider the following code (I assume that variable x is not defined in enclosing scope so that x is defined in local scope):
julia> v = []
Any[]
julia> for i in 1:2
x = i
push!(v, () -> x)
end
julia> v[1]()
1
julia> v[2]()
2
Whe have created two anonymous functions and all works as you probably expected.
Now let us check what would happen in Python:
>>> v = []
>>> for i in range(1, 3):
... x = i
... v.append(lambda: x)
...
>>> v[0]()
2
>>> v[1]()
2
The result might surprise you. Both anonymous functions return 2. This is a consequence of not creating a local variable with a new binding in each iteration of the loop.
However, if in Julia you were working in REPL and x were defined in global scope you would get:
julia> x = 0
0
julia> v = []
Any[]
julia> for i in 1:2
x = i
push!(v, () -> x)
end
julia> v[1]()
2
julia> v[2]()
2
just like in Python.
The other consideration, as explained in the other answer is performance. But most likely performance critical code is written inside a function anyway, and the discussed performance considerations are only relevant in global scope.
EDIT
This is a design choice of Matlab, quoting from https://research.wmz.ninja/articles/2017/05/closures-in-matlab.html:
When an anonymous function is created, the immediate values of the referenced local variables will be captured. Hence if any changes to the referenced local variables made after the creation of this anonymous function will not affect this anonymous function.
So as you can see in Matlab there is a difference of anonymous function vs. a closure, which does something different:
When a nested function is created, the immediate values of the referenced local variables will not be captured. When the nested function is called, it will use the current values of the referenced local variables.
In Julia there is no such difference as you can see in the examples above.
And quoting the documentation of Matlab https://www.mathworks.com/help/matlab/matlab_prog/anonymous-functions.html:
Because a, b, and c are available at the time you create parabola, the function handle includes those values. The values persist within the function handle even if you clear the variables:
(but I think it is not as explicit as the explanation I linked above)

How to get function from function type?

Suppose I have hold of the concrete type of a normal, named Julia function, F = typeof(f). Is it possible to get back f from F? I assume this should work in principle, since F is a singleton type.
You can go with Core.Compiler.singleton_type function which returns the instance field for a concrete DataType. I think this function is used for the same objective as yours (not only for this objective) in Core.Compiler during compilation. This is probably considered "more internal" than directly accessing instance field.
julia> Core.Compiler.singleton_type(typeof(sum))
sum (generic function with 13 methods)
I would use F.instance, but maybe there is some better solution (as this one is using the internals).

Parameters of function in Julia

Does anyone know the reasons why Julia chose a design of functions where the parameters given as inputs cannot be modified?  This requires, if we want to use it anyway, to go through a very artificial process, by representing these data in the form of a ridiculous single element table.
Ada, which had the same kind of limitation, abandoned it in its 2012 redesign to the great satisfaction of its users. A small keyword (like out in Ada) could very well indicate that the possibility of keeping the modifications of a parameter at the output is required.
From my experience in Julia it is useful to understand the difference between a value and a binding.
Values
Each value in Julia has a concrete type and location in memory. Value can be mutable or immutable. In particular when you define your own composite type you can decide if objects of this type should be mutable (mutable struct) or immutable (struct).
Of course Julia has in-built types and some of them are mutable (e.g. arrays) and other are immutable (e.g. numbers, strings). Of course there are design trade-offs between them. From my perspective two major benefits of immutable values are:
if a compiler works with immutable values it can perform many optimizations to speed up code;
a user is can be sure that passing an immutable to a function will not change it and such encapsulation can simplify code analysis.
However, in particular, if you want to wrap an immutable value in a mutable wrapper a standard way to do it is to use Ref like this:
julia> x = Ref(1)
Base.RefValue{Int64}(1)
julia> x[]
1
julia> x[] = 10
10
julia> x
Base.RefValue{Int64}(10)
julia> x[]
10
You can pass such values to a function and modify them inside. Of course Ref introduces a different type so method implementation has to be a bit different.
Variables
A variable is a name bound to a value. In general, except for some special cases like:
rebinding a variable from module A in module B;
redefining some constants, e.g. trying to reassign a function name with a non-function value;
rebinding a variable that has a specified type of allowed values with a value that cannot be converted to this type;
you can rebind a variable to point to any value you wish. Rebinding is performed most of the time using = or some special constructs (like in for, let or catch statements).
Now - getting to the point - function is passed a value not a binding. You can modify a binding of a function parameter (in other words: you can rebind a value that a parameter is pointing to), but this parameter is a fresh variable whose scope lies inside a function.
If, for instance, we wanted a call like:
x = 10
f(x)
change a binding of variable x it is impossible because f does not even know of existence of x. It only gets passed its value. In particular - as I have noted above - adding such a functionality would break the rule that module A cannot rebind variables form module B, as f might be defined in a module different than where x is defined.
What to do
Actually it is easy enough to work without this feature from my experience:
What I typically do is simply return a value from a function that I assign to a variable. In Julia it is very easy because of tuple unpacking syntax like e.g. x,y,z = f(x,y,z), where f can be defined e.g. as f(x,y,z) = 2x,3y,4z;
You can use macros which get expanded before code execution and thus can have an effect modifying a binding of a variable, e.g. macro plusone(x) return esc(:($x = $x+1)) end and now writing y=100; #plusone(y) will change the binding of y;
Finally you can use Ref as discussed above (or any other mutable wrapper - as you have noted in your question).
"Does anyone know the reasons why Julia chose a design of functions where the parameters given as inputs cannot be modified?" asked by Schemer
Your question is wrong because you assume the wrong things.
Parameters are variables
When you pass things to a function, often those things are values and not variables.
for example:
function double(x::Int64)
2 * x
end
Now what happens when you call it using
double(4)
What is the point of the function modifying it's parameter x , it's pointless. Furthermore the function has no idea how it is called.
Furthermore, Julia is built for speed.
A function that modifies its parameter will be hard to optimise because it causes side effects. A side effect is when a procedure/function changes objects/things outside of it's scope.
If a function does not modifies a variable that is part of its calling parameter then you can be safe knowing.
the variable will not have its value changed
the result of the function can be optimised to a constant
not calling the function will not break the program's behaviour
Those above three factors are what makes FUNCTIONAL language fast and NON FUNCTIONAL language slow.
Furthermore when you move into Parallel programming or Multi Threaded programming, you absolutely DO NOT WANT a variable having it's value changed without you (The programmer) knowing about it.
"How would you implement with your proposed macro, the function F(x) which returns a boolean value and modifies c by c:= c + 1. F can be used in the following piece of Ada code : c:= 0; While F(c) Loop ... End Loop;" asked by Schemer
I would write
function F(x)
boolean_result = perform_some_logic()
return (boolean_result,x+1)
end
flag = true
c = 0
(flag,c) = F(c)
while flag
do_stuff()
(flag,c) = F(c)
end
"Unfortunately no, because, and I should have said that, c has to take again the value 0 when F return the value False (c increases as long the Loop lives and return to 0 when it dies). " said Schemer
Then I would write
function F(x)
boolean_result = perform_some_logic()
if boolean_result == true
return (true,x+1)
else
return (false,0)
end
end
flag = true
c = 0
(flag,c) = F(c)
while flag
do_stuff()
(flag,c) = F(c)
end

Puzzling results for Julia typeof

I am puzzled by the following results of typeof in the Julia 1.0.0 REPL:
# This makes sense.
julia> typeof(10)
Int64
# This surprised me.
julia> typeof(function)
ERROR: syntax: unexpected ")"
# No answer at all for return example and no error either.
julia> typeof(return)
# In the next two examples the REPL returns the input code.
julia> typeof(in)
typeof(in)
julia> typeof(typeof)
typeof(typeof)
# The "for" word returns an error like the "function" word.
julia> typeof(for)
ERROR: syntax: unexpected ")"
The Julia 1.0.0 documentation says for typeof
"Get the concrete type of x."
The typeof(function) example is the one that really surprised me. I expected a function to be a first-class object in Julia and have a type. I guess I need to understand types in Julia.
Any suggestions?
Edit
Per some comment questions below, here is an example based on a small function:
julia> function test() return "test"; end
test (generic function with 1 method)
julia> test()
"test"
julia> typeof(test)
typeof(test)
Based on this example, I would have expected typeof(test) to return generic function, not typeof(test).
To be clear, I am not a hardcore user of the Julia internals. What follows is an answer designed to be (hopefully) an intuitive explanation of what functions are in Julia for the non-hardcore user. I do think this (very good) question could also benefit from a more technical answer provided by one of the more core developers of the language. Also, this answer is longer than I'd like, but I've used multiple examples to try and make things as intuitive as possible.
As has been pointed out in the comments, function itself is a reserved keyword, and is not an actual function istself per se, and so is orthogonal to the actual question. This answer is intended to address your edit to the question.
Since Julia v0.6+, Function is an abstract supertype, much in the same way that Number is an abstract supertype. All functions, e.g. mean, user-defined functions, and anonymous functions, are subtypes of Function, in the same way that Float64 and Int are subtypes of Number.
This structure is deliberate and has several advantages.
Firstly, for reasons I don't fully understand, structuring functions in this way was the key to allowing anonymous functions in Julia to run just as fast as in-built functions from Base. See here and here as starting points if you want to learn more about this.
Secondly, because each function is its own subtype, you can now dispatch on specific functions. For example:
f1(f::T, x) where {T<:typeof(mean)} = f(x)
and:
f1(f::T, x) where {T<:typeof(sum)} = f(x) + 1
are different dispatch methods for the function f1
So, given all this, why does, e.g. typeof(sum) return typeof(sum), especially given that typeof(Float64) returns DataType? The issue here is that, roughly speaking, from a syntactical perspective, sum needs to serves two purposes simultaneously. It needs to be both a value, like e.g. 1.0, albeit one that is used to call the sum function on some input. But, it is also needs to be a type name, like Float64.
Obviously, it can't do both at the same time. So sum on its own behaves like a value. You can write f = sum ; f(randn(5)) to see how it behaves like a value. But we also need some way of representing the type of sum that will work not just for sum, but for any user-defined function, and any anonymous function. The developers decided to go with the (arguably) simplest option and have the type of sum print literally as typeof(sum), hence the behaviour you observe. Similarly if I write f1(x) = x ; typeof(f1), that will also return typeof(f1).
Anonymous functions are a bit more tricky, since they are not named as such. What should we do for typeof(x -> x^2)? What actually happens is that when you build an anonymous function, it is stored as a temporary global variable in the module Main, and given a number that serves as its type for lookup purposes. So if you write f = (x -> x^2), you'll get something back like #3 (generic function with 1 method), and typeof(f) will return something like getfield(Main, Symbol("##3#4")), where you can see that Symbol("##3#4") is the temporary type of this anonymous function stored in Main. (a side effect of this is that if you write code that keeps arbitrarily generating the same anonymous function over and over you will eventually overflow memory, since they are all actually being stored as separate global variables of their own type - however, this does not prevent you from doing something like this for n = 1:largenumber ; findall(y -> y > 1.0, x) ; end inside a function, since in this case the anonymous function is only compiled once at compile-time).
Relating all of this back to the Function supertype, you'll note that typeof(sum) <: Function returns true, showing that the type of sum, aka typeof(sum) is indeed a subtype of Function. And note also that typeof(typeof(sum)) returns DataType, in much the same way that typeof(typeof(1.0)) returns DataType, which shows how sum actually behaves like a value.
Now, given everything I've said, all the examples in your question now make sense. typeof(function) and typeof(for) return errors as they should, since function and for are reserved syntax. typeof(typeof) and typeof(in) correctly return (respectively) typeof(typeof), and typeof(in), since typeof and in are both functions. Note of course that typeof(typeof(typeof)) returns DataType.

Recursive function parameters

I made a simple recursive function, and expected it to work (but it doesn't):
open System
open System.Threading
let f =
let r = Random()
let rec d =
printfn "%d" (r.Next())
Thread.Sleep(1000)
d
d
f
With the help of Intellisense I ended up with the following working function (but without understanding why previous function didn't work):
open System
open System.Threading
let f : unit =
let r = Random()
let rec d() =
printfn "%d" (r.Next())
Thread.Sleep(1000)
d()
d()
f
So why do I need to explicitly state unit and ()?
In the first version, you declared a recursive object (let rec d), or a value. You're saying that the d object is recursive, but how an object could be recursive? How does it call itself? Of course, this doesn't make sense.
It's not possible to use recursive objects in F# and this is the reason why your first version doesn't work.
In the second version, you declared a recursive function (let rec d()). Adding (), you're explicitly stating that d is a function.
Furthermore you explicitly stated, with unit, that the function f (called just once) will not return anything, or, at least, you're saying that f will return a value of a not specific type. In F#, even the simplest functions must always return a value.
In your case, F# will try to infer the type that f will return. Because there's no specific type annotation and your f is not doing something (like a calculation) that will return a specific value using a specific type, the F# compiler will assign a generic return type to f, but your code is still ambiguous and you have to specify the unit type (the simplest type that a F# function could return) to be more specific.
The value restriction error is related indeed to F#'s powerful type inference. Please have a look at this interesting article about this error.
In your first attempt, you define not a function, but a value. The value d is defined in terms of itself - that is, in order to know what d is, you need to first know what d is. No wonder it doesn't work!
To make this a bit more clear, I will point out that your definition is of the same kind as this:
let x = x
Would you expect this to work?
In your second attempt, you gave d a parameter. It is the parameter that made it a function and not a value. Compare:
let rec x() = x()
This will still cause a stack overflow when executed, but at least it will compile: it's a function that unconditionally calls itself.
You didn't have to give it specifically a unit parameter, any parameter would do. You could have made it a number, a string, or even a generic type. It's just that unit is the simplest option when you don't care what it is.
And you didn't actually need to annotate f with a type. That was an extraneous step.
In conclusion, I'd like to point out that even in your second code block, f is still a value, not a function. In practical terms it means that the code inside f will be executed just once, when f is defined, and not every time you mention f as part of some other expression, which is apparently what you intuitively expect.

Resources