Just thought to throw some info in here for no reason.
The way Felix implements closures is non-obvious and not efficient
for a subtle reason. When you do this:
fun f(x:int)=>x;
var g = f;
println$ g1;
Felix create an object of C++ class f on the heap, and stores a pointer
to it in variable g. It has a method
int f::apply(int x) { return x; }
so when you do the println, Felix calls
g->apply(1)
right?
Wrong. Felix calls this:
g->clone()->apply(1)
Here clone() says:
return new f(*this);
which creates a self-copy on the heap using the copy constructor,
and returns a pointer to this new object.
This ensures that we always have a virgin function when we call the apply
method.
The question is why bother? After all, unlike procedures, functions are invoked
immediately
calling the apply method, which pushes the return value on the stack. The only
advantage
of the clone() call is that Felix values that didn't get initialised explicitly
retain their C++
default constructor values, rather than the last value that happened to be
assigned to them.
Right?
WRONG. (You knew that was coming, eh?)
In the example above, there's no reason to clone() the function.
Well, what about recursive functions? Wouldn't it be bad if a recursion
got a dirty, half initialised object?
fun f(x:int) => if x == 1 then 1 else x * f (x-1) endif;
Well .. no. You see, this is implemented by calling
x * (new (stuff) f)->apply(x-1)
so it created a new object, because the call to f here is a direct call, so the
closure object (new thing) is created "on the fly". So we actually get a stack
of closures, no problem.
And you see, that's the key to the problem. Read again "direct call".
Now suppose it is an *indirect call*:
fun f(x:int) => if x == 1 then 1 else x * g (x-1) endif;
var g = f;
Now, you can see that f is calling g, but an f object is stored in g.
So half way through executing the f stored in g, g is reused.
In this case probably little harm, but a more complex function ..
well the local variables would be destroyed (remember, the
return address goes on the machine stack so that's safe).
So that's why the object is cloned: to ensure each invocation uses
a fresh "stack frame".
So, what's a generator?
Answer: its a function where
clone() { return this; }
i.e. the frame isn't cloned, by a trivial hack to the generated code.
So the state is preserved between calls.
--
john skaller
[email protected]
http://felix-lang.org
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language