DietLISP icon indicating copy to clipboard operation
DietLISP copied to clipboard

Toy lisp interpreter / PLT playground

DietLISP

DietLISP is an interpreter for an untyped Lisp variant written in Haskell. This is a toy project that I started working on during my winter break in the Doon valley.

The Language

DietLISP is a minimalist, lazy, purely functional lisp. It does not have a static type system. The distinguishing feature of DietLISP is that all executable forms in it are semi-operatives.

Semi-Operatives


The notion of a *semi-operative* is similar to that of an *operative*
in the Kernel programming language.  Semi-operatives combine macros
and functions into one unified concept but are more restrictive than
*fexprs*.

I like to describe a semi-operative as *a hook into the denotational
semantics of the interpreter*.  You see, in a regular lisp, functions
don't directly influence the interpreter and macros are hooks into the
*parser*.  Semi-operatives go a little deeper and we get a
macro-function hybrid.

A semi-operative, like a macro, gets the bits of AST it has been
invoked with, and (unlike a macro) the environment where it was
invoked.  It has access to functions that evaluate an AST in a
specific environment (``eval`` and ``eval*``), and functions to extend
the environment (``add-binding``).  The result of the semi-operative
invocation is the value (rather, the *domain value*) the interpreter
gets by executing the semi-operative.  I think it is useful to think
of semi-operatives as hooks that arbitrarily map program phrases into
domain values.

As an example of what this makes possible, look at
``samples/SimpleOperatives.dlisp``::

  ;; The #builtin# directives return an semi-operative defined inside
     the interpreter in Haskell.  global-bind binds them to the global
     lexical scope.
  (global-bind let (#builtin# #let#))
  (global-bind eval (#builtin# #eval#))

  (global-bind wormhole (operative () env (eval env x)))

  (let (x 42) (wormhole)) ;; Prints 42, since wormhole evaluates x in
    the environment inside let x (42)

``eval (ast, env)`` evaluates ``ast`` in the environment ``env``.
``eval* (ast, env)`` evaluates ``ast`` in the context of the current
environment and then evaluates this *result* in the context of ``env``
(``ast`` not evaluating to an AST is an error).  This allows you to
write functions this way::

   (operative (number) env
     (let (evaluated-number (eval* env number))
       (+ evaluated-number 5)))

In the above case, ``eval*`` first evaluates ``number`` in the current
lexical scope (which gives us the AST passed to the semi-operative
during evaluation) and then evaluates *that* ast in ``env`` (which
gives us some result to which we then add 5).

Conditional evaluation makes writing macros easy.  Here is a ``when``
macro::

  (global-bind when
    (operative (condition action) env
      (if (eval* env condition)
        (eval* env action)
        0)))

which evaluates to ``action`` if ``condition`` evaluates to
``true`` else evaluates to ``0``.

Two primitives useful when writing macros are ``unwrap-ast`` and
``wrap-to-ast``, which convert an AST to a domain value and
vice-versa; respectively.  A list can't be directly evaluated but
needs to be *wrapped* into an AST before evaluation.  Similarly, an
AST is impervious to direct introspection till it has been *unwrapped*
into a regular domain value.


Hopes and fears

So, will this all work out? I don't know. I have a hunch that semi-operatives might lead to a cleaner and more orthogonal language; I'll keep updating this README as I explore and learn more.

Texts

As it is probably evident by now, this is more of a academic project. I found the following texts very helpful:

  • Structure and Interpretation of Computer Programs (Hal Abelson, Jerry Sussman, Julie Sussman)
  • Essential of Programming Languages (Daniel P. Friedman, Mitchell Wand, Christopher T. Haynes)
  • Design Concepts in Programming Languages (Franklyn Turbak, David Gifford, Mark A. Sheldon)

I'd at recommend at least the first two texts to any serious software engineer.