Revisiting Primitives and Special Forms in GoLisp

14 Jul 2015

by Dave Astels

For V1.0, I revisited the way primitive functions are handled to make them work more like regular functions (i.e. defined in Lisp using define/lambda). The visible change if you are writing primitives is that they now recieve evaluated arguments. I.e. they use applicative order of evaluation now. This is as it should be.

The things that are special forms are now actually special forms which use normal evaluation order: they receive unevaluated arguments and have complete control over if and when their arguments get evaluated. Special forms are declared very much like primitives are, but using a different function:

To declare a special form, use the MakeSpecialForm function instead of MakePrimitiveFunction.

This approach drastically simplifies primitives. Compare the previous and current implementations of a simple function: car.

Here’s the previous version:

MakePrimitiveFunction("car", 1, CarImpl)

func CarImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	l, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	return WalkList(l, "a"), nil
}

And here’s the current version using applicative evaluation order:

MakePrimitiveFunction("car", "1", CarImpl)

func CarImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	return WalkList(Car(args), "a"), nil
}

No call to Eval and no checking of the possible resulting error. That’s now done by the evaluator before calling the implementation function.

At the moment the following are special forms.

  • cond
  • case
  • if
  • when
  • unless
  • lambda
  • named-lambda
  • define
  • defmacro
  • let
  • let*
  • letrec
  • begin
  • do
  • apply
  • eval
  • global-eval
  • ->
  • =>
  • definition-of
  • quote
  • quasiquote
  • unquote
  • unquote-splicing
  • expand
  • set!
  • set-car!
  • set-cdr!
  • set-nth!
  • time
  • profile

The function append! is implemented as a special form as a hack to be able to get hold of the name the initial list argument is bound to so that it can be updated.

Special forms are currently part of the V1.0 branch.