Functions

functions in citron are simply code blocks given name.

to make a function, you may simply assign a code block to a variable:

var fn is {:x:y Pen writeln: (x abs - y). ^x pow: y. }.

and to call a function, simply send a message to it:

fn applyTo: 1 and: 2. #apply arguments over several keyword args fn applyAll: [1, 2]. #apply all arguments at once with a tuple

If not enough arguments are passed, depending on the message, you might get either an exception, or have the parameter simply evaluate to Nil

(CodeBlock::'applyAll:' will throw an exception if more or less arguments are provided)

Functions will behave just like code blocks in every aspect.

The `self' parameter

In order to mitigate the problem with me always referencing the current block, you may simply add self as the first parameter to the block, like so:

fn is {:self:par Pen writeln: par. #`self' here is a direct reference to this block ^{ ^const self. #and it still references that block }. }. #self is implicitly filled, acts as if it never existed fn applyTo: 1024. #par gets assigned 1024

Varargs

To collect all extra arguments in an array, you may provide a vararg *argument_name in the formal parameters

fn is {:x:*ys Pen writeln: 'I got some $$x and a bunch of $$ys'. }.

Evaluation of different callings:

⠒⠂fn is {:x:*ys Pen writeln: 'I got some $$x and a bunch of $$ys'.}. [_:Block] ⠕ fn applyTo: 1. I got some 1 and a bunch of Array new [_:Block] ⠕ fn applyTo: 1 and: 2. I got some 1 and a bunch of Array ← 2 [_:Block] ⠕ fn applyAll: [1,2,3]. I got some 1 and a bunch of Array ← 2 ; 3 [_:Block] ⠕ fn applyAll: [1]. I got some 1 and a bunch of Array new [_:Block]

Calling by Reference vs Calling by Value

Normally, "simple" objects are passed by value, and the rest are passed by reference;

However, by prepending an ampersand (&) to an argument, it will always be passed as a reference.

Note

Lambdas and Lexical Blocks will not accept reference arguments

Here's a handy table:

Type/BehaviourBehaviour under :argBehaviour under :&argBehaviour under :*arg
NilReferenceReferenceReference
StringCopyReferenceReference
BooleanCopyReferenceReference
NumberCopyReferenceReference
ArrayReferenceReferenceReference
Any other typeReferenceReferenceReference

Calling functions

There are several ways to interact with functions, each can be used for some very specific purpose:

  1. Simply using the different messages that CodeBlock provides: applyTo:[and:]* and applyAll:

    • func applyTo: 1 and: 2
    • func applyAll: [1, 2]
  2. Using the call shorthand (expr [...] by default)

    • func [1, 2]
  3. using the infix function call (for binary functions)

    • 1 `func` 2
    • This mode can be configured (with a pragma) to lazily pass the arguments
  4. using the lazy call shorthand (not configurable) (expr {...})

    • func {1, 2}

Example outlining all the above methods

⠕ var fun0 is \:x:y x + y [_:Block] ⠕ fun0 applyTo: 1 and: 2 3 ⠕ fun0 applyAll: [1, 2] 3 ⠕ fun0 [1, 2] 31 `fun0` 2 3 # lazy evaluation style => explicit evaluation ⠕ var fun0 is \:x:y x evaluate + y evaluate [_:Block] # a pragma in eval:!pragmaOnce::declare lazyev fun01 `fun0` 2 3 ⠕ fun0 {1, 2} 3 ⠕ var fun_ref is {:&x:y ^(x +=: 1) + y.} [_:Block] ⠕ var x is 1 1 ⠕ var y is 2 2 ⠕ fun_ref[x, y] 4 ⠕ x 2 ⠕ y 2