Skip to content

operators

Bill Hails edited this page Nov 3, 2024 · 23 revisions

You can define your own prefix, infix and postfix operators. Here's an example I've already added to the preamble:

fn factorial (n) {
    let
        fn h {
            (0, a) { a }
            (n, a) { h(n - 1, n * a) }
        }
    in
        h(n, 1)
}

postfix 120 "!" factorial;

and an example:

$ ./cekf --exec='print 100!'
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

because every language should have a postfix factorial operator!

I've also removed special treatment for @, @@, prefix < and prefix > from the parser and put them in the preamble as

infix right 90 "@" cons;
infix right 80 "@@" append;
prefix 55 "<" car;
prefix 55 ">" cdr;

The syntax is pretty self explanatory I think, the number is precedence and I'll need to document the precedence of the core operators when they settle down but for now you can look in src/pratt_parser.c. The quoted operator can be anything that doesn't include whitespace and doesn't start with a digit or an open or close parenthesis (Unicode general category "Ps" or "Pe").

The semantics are also pretty self-evident, for example a @ b gets transformed to cons(a, b) so the cons in the infix declaration is just an expression that gets applied to its (two) arguments. Anything that works in the place of a function can be used, specifically type constructors, macros and anonymous functions.

A few caveats:

  1. You can't redefine an existing operator with the same fixity.
  2. Because of the way parsing works you can't declare the same operator as both infix and postfix (the other two combinations, prefix-infix and prefix-postfix are fine).
  3. If you define an operator with the same name as an existing function it will mask the function (but can still be used as an operator).
  4. Because operator rewriting is a purely textual replacement at the point of use, the replacement will pick up whatever definition is current when the operator is invoked. This is more of a bug than a feature to be honest, and I'd very much like to fix it.

Next: Macros

Clone this wiki locally