in is syntactically part of the
let expression. It
works like this:
let i = 1 in expr(i)
in seperates the definition of the variable or
function from the code which uses the variable or function.
When a Calculon script has multiple let expressions, what's actually happening is that all expressions after the first are embedded inside the body of the first. These two equivalent code blocks should make things clearer:
let a = 1 in let b = 2 in let c = 3 in a + b + c
let a = 1 in (let b = 2 in (let c = 3 in (a+b+c)))
If you prefer, it's possible to think of
in as terminating the
definition part of a let expression.
How do I do loops efficiently?
Calculon is a pure functional language and so doesn't have mutable variables. This means there's no such thing as a for...next loop.
In order to calculate something iteratively, therefore, you have to use recursion. The suggested code is as follows:
let f(n, accumulator) = if condition then accumulator /* stop value */ else f(n+1, g(accumulator))
This will repeatedly evaluate
condition is true, incrementing
n each time
round the loop.
It is very important that the recursive call to
f() on the last
line is performs no calculations on the return value. This is tail
recursion, and the compiler knows how to optimise for it, converting the code
above into a single loop. If you don't do this, then it will have
to compile that call to
f() as an actual function call, which is
much slower and may cause your script to run out of stack (which will normally
cause your program to crash).
This is good:
...and this is bad:
f(n+1, g(accumulator)) + 1
See fractal.cal for a real example.
When I'm calling out to an external function, how do I know where I'm being called from?
Frequently it's desirable to pass context through from the caller of a Calculon script to a function that's being called by a Calculon script. For example: the function that looks up a value in a 2D texture needs to know which texture to use.
Currently there's no way to pass this information through Calculon (if you need it, please ask, I'd like to know if there are any use cases). However it's pretty simple to pass the relevant information via a backchannel. TLS works nicely for this; wrap the Calculon script in an object, and then just before calling the script set a TLS variable to the address of the object. Now when the external function is called, it can look up the TLS value to determine which instance of the script was called.
thread_specific_ptr class is probably the way to go here.)
Alternatively, if you don't care about threading, you could just use a global variable.
How do I do optional output parameters?
Give them the same name as an input parameter. That way any output parameters which aren't set by the script will take their value from the input parameter.
For example, this script:
...is an entirely valid implementation of this signature:
(x:real, y:real, v:vector*5): (x:real, y:real, v:vector*5)