How does in work?

in is syntactically part of the let expression. It works like this:

let i = 1 in expr(i)

The 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 g() on accumulator until 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:

f(n+1, g(accumulator))

...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.

(Boost's 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:

return

...is an entirely valid implementation of this signature:

(x:real, y:real, v:vector*5): (x:real, y:real, v:vector*5)

Previous page Next page