Lise (short for (list (embedding)) is a dynamic functional-first, byte-compiled language, embedded in an extented version of the pure OO environment of Pharo. Lise pushes MOGs and ASL to their limits by exploring the ambiguity, conflicts and interplay of multi-paradigm (functional, OO, procedural) language design.

Lise raison d'être ? Study SICP with a Xerox flavor, while re-inventing some Bell labs utilities.

Here is the extented version of the postcard we saw earlier, this time written entirely in Lise:

lisePostcard

For quick comparison, here is the pure Pharo version:

gTraitsPostcard

Right from this first example, we can see some intresting new semantics that are made possible from the compiled embedding of a lisp dialect into an OO envrionment, that are not found in a OO-only or functional-only environments:

lisePostcardAnnotated

Smiley Scoping

The most obvious is the smiley (: alternative to parentesized function calls, that serves both for scoping Lise syntax within Pharo code:

In the example above the return caret is a Pharo statement, while the rest of the method from (:begin ...` till the end ) is a Lise expression.

But also for scoping Pharo syntax from within Lise:

In the example above this can be seen in the (:t copyFrom: 1 to: t size -1) Pharo expression, embedded within the wider Lise expression.

As we will see later this scoping can be extented to accomodated other kind of syntaxes as well. It also happens to be a perfect starting point for researching ambiguity arising in multi-paradigm language design, since (at least in this case) the explicity scoping can be infered at run-time even if parsing is ambiguous at compile-time.

Polymorphic Functional Dispatch

Since Lise is embedded inside an OO language, there is no reason to restrict ourselves only to lambdas as heads of functional invocations.

In fact in Lise, every entity responding to the Lise-specific #polyValueWithArguments: message can be used as a functional application head. This includes:

  1. Lambdas (this is exactly how normal functional invocation works in Lise)
  2. Symbols (which accepts an object to be applied to reflectively as a second argument, plus the rest of the arguments if any).
  3. Every other Pharo object (a default #polyValueWithArguments: message is defined in the root hierarch accepting a symbol to reflectively perform as a second argument, plus the rest of the arguments if applicable)

These can be seen in numerous places in the above example, such as (self 'bSize) or ('bSize super) , ('class #a) , (self 'halt) and (y 'first).

Primary Value Sharing

This unique interplay between the two paradigms, also produces intresting syntactic and semantic forms, when we consider the use of primary values that the two languages share:

PolyInvoc

Notice for example in the above mixed-paradigm unit-test (#assert: and #= messages are Pharo expressions, while the rest is a Lise expression with scoped Pharo embeddings), the use of Pharo block literals and dynamic arrays (seen on line 2):

([:x | x first - 5] {'string' size})

There is no scoped embedding in line 2 (ie (:...) smiley scoping), yet we can mix pharo code (inside the pharo-block and the dynamic array) with a functional Lise invocation (the block is the head and will thus be evaluated with the dynamic array as its argument).

This non-scoped embedding emerges automatically in an environment where OO expressions and functional expressions share the same primary value syntax and semantics.

On a side-note: the curious reader might have noticed that the #+ Lise function is variadic in the above example. This is still another Lise extention, since Pharo does not natively support variadic lambdas.

Primary value sharing is beautifully expressed in the Lise Recognizer and Compiler (assembled of course through the use of GrammarTraits):

liseRecognizer

The LiseExpressions Recognizer by itself, operates only over lambdas, lists, numbers and strings. The above GrammarTrait composition says that the resulting sum of the extented PharoGrammar with the LiseExpresssions grammar should use the full set of Pharo primary values instead ! Not shown above are the overriden rules for the composition which in this case includes an extented version of what constitutes an <expression> for the resulting language.

liseCompiler

For the extented LiseCompiler the GrammarTrait composition is a straight- forward sum. Since any conflicts or overrides where succefully handled at the recognizer level above.

As a first step, towards porting larger functional systems into Lise, for teaching and research. We also ported P. Norvig’s test-suite for lispy implementations, to test Lise:

liseNorvig

Notice the mixing of Lise expressions with the |=? custom pharo operator in the above example (used as a short-hand for assert:equals:) simplifying unit-testing of Lise expressions.

The punchline in the Lise Postcards

As we noted earlier, smiley scoping (:..) can be also used to accomodated other syntactical extentions as well. Here is a Lise postcard mixing the imperative extentions we discussed in the GrammarTraits overview. With the exception of the pragma there is not a single native Pharo expression in this method (only functional and procedural extentions):

liseAltPostcard

And finally the postcard with all three paradigms combined, Lise (functional) Pharo (OO), and our imperative/procedural extentions:

liseAltMixedPostcard

While we are not expecting developers to mix paradigms so aggresively, this last experiment, explores the boundaries of what is possible with MOGs, Gray and GrammarTraits. It also shows that our results are generalizable. They can also be applicable in languages with a more imperative (Java, C# etc) or functional base (Racket, Clojure …), instead of only a pure OO one (like Pharo).

Indeed in Lise there is now no real distinction between the OO host-language and the functional or procedural extentions, since they are all implemented the exact same way (through MOGs, ASL and GrammarTraits).