Go to the first, previous, next, last section, table of contents.

Understanding Arities

One particularly insidious class of bugs are those in which a function trashes some stack slot that doesn't "belong" to it, or returns one more value than expected, or such.

These bugs can be quite difficult to track down, since it may be hundreds of nested subroutine calls later until the problem triggers an error, by which time tracking down the long-vanished culprit may be an arduous task indeed.

To help prevent such problems before they happen, Muq MUF implements functionarities. ("Arity" is a generalization of "unary", "binary", "trinary"...) The arity of a function is a synopsis of how many values it accepts and returns on the stack.

Muq expects each function to have a fixed arity (to always consume and return the same number of arguments), and it normally computes this arity for each function as it compiles it, and saves the result for future reference. If it finds an inconsistency, such as a function which sometimes returns three values, and sometimes four, it will issue an error message.

You can tell MUF what arity you think your function has, and it will also issue an error message if it disagrees with your opinion. You can also tell it to accept some arity for the function, and not bother checking itself. This is sometimes useful in cases which MUF is not smart enough to analyse itself.

The simplest arity-declaration syntax looks like:

: x { $ $ -> $ } * ;

This declares a function which accepts two values on the stack, and returns one. You may also declare functions which operate on blocks:

: |r { [] -> [] } |reverse ;

A functions may have both block and scalar arguments:

: |pr { [] $ -> [] } |push |reverse ;

(In this case, the blocks must always precede the scalar arguments, on both sides of the arrow.)

Some functions never return. (They may loop forever, or they may do a throw past the caller.) The syntax for declaring this looks like:

: err { -> @ } [ "Woops! | ]throw-error ;

(Think of the '@' as a black hole that the program falls into.)

This is to be avoided where reasonably possible, but very occasionally you may have a function which genuinely needs to accept or return a variable number of arguments. (The compile-} function in 00-Core-muf.muf is a moderately good example.) The syntax for declaring this is:

: yikes { -> ? } if pop fi ;

Even more rarely, you may have a function which deposits or eats a '[':

: my-[ { -> [ } [ ;
: my-| { -> | } | ;
: my-] { -> ] } ] ;

Finally, if you want MUF to simply accept your declared arity without checking, either because the function is beyond MUF's current ability to understand or because you've found a good reason to lie to it, end your arity declaration with a '!':

: lie  { $ -> $ ! } + ; ( It is really { $ $ -> $ } )
: soon {   ->  ! } ;   ( It's not finished, but won't return when done.)

FUTURE DIRECTIONS: I expect to eventually revamp the compiler so that

: x { a b -> c }  a b + -> c ;

will work: The compiler will automatically pop a and b into local variables with those names at the top of the function, and push local variable c onto the stack before returning. I'm currently only allowing '$' for scalar arguments in arity declarations, in order to leave room for upgrading to this approach without breaking existing MUF code at that point.

Go to the first, previous, next, last section, table of contents.