Nutmeg Design Goals - Spicery/Nutmeg GitHub Wiki

Overview

  • Nutmeg is intended to help teach the core ideas of functional programming to people familiar with procedural programming via side-by-side detailed comparisons of functional and procedural programming.
  • In contrast to other cross-paradigm languages, the purity of Nutmeg functions is enforced and laziness is the default.
  • It also introduces the finesse, which teaches how to program procedurally with many of the benefits of the functional style.
  • Key features of functional programming, laziness and immutability are made available to the procedural programmer in fine-grained versions: deferral and sealing.
  • Nutmeg emphasises the value of immutability and non-assignability by making these the default. For example, variables are either assignable variables (var) or non-assignable values (val), the being the default.
  • All built in data-types come in mutable and immutable flavours with the latter taking the short name e.g. MutableList vs List.
  • Nutmeg subscribes to the Dollin Principle (see below).

Procedures

Nutmeg procedures provide unrestricted access to the full range of features of the language.

def hello( name ) =>>
    println( 'Hello, ', name )
enddef

The conceit of functional programming is that unrestricted access is difficult to rigorously control and makes it impractical to reason about behaviour and that the cure is a radical return to our mathematical roots.

Functions

Nutmeg provides support for defining pure functions out of the box. Pure functions have recursively immutable (const) inputs and return recursively immutable outputs, cannot mutate existing store and cannot use procedural I/O. Functions are lazy by default, although this can be overridden.

function nfib( n ) =>>
    if n <= 1 then
        1
    else
        nfib( n - 1 ) + nfib( n - 2 ) + 1
    endif
endfunction

Finesses - Hybrid Functions

A key design objective of Nutmeg is to support a coding style that is halfway between functional and imperative. It is defined as functional-on-the-outside but imperative-on-the-inside. In other words, in the body of a hybrid-definition, or finesse, we can use some imperative programming features but they are encapsulated so that you can treat the result as purely functional.

For example, we can write the classic bubblesort because the mutable store stays encapsulated within the dynamic invocation of the finesse.

@[finesse]                             ### Finesses are declared by an attribute, syntax TBD.
def bubblesort( list ) =>>
    vector := MutableList( **list )    ### **x means all the values of x, syntax TBD
    N := vector.length
  
    # Traverse through all array elements 
    for i in [ 0 ..< N-1 ] do
        ### Here: the elements from [ N-i ..< N ] are sorted.
        for j in [ 0 ..< N-1-i ] do
            ### Here: the biggest element in the range [ 0 ... j ] is at j.
            if vector[ j ] > vector[ j+1 ] then
                vector.swap( j, j+1 )
            endif
            ### Here: the biggest element in the range [ 0 ... j+1 ] is at j + 1.
        endfor
        ### Here: the elements from [ N-(i+1) ..< N ] are sorted.
    endfor

    List( vector )
end

The Dollin Principle

The Dollin Principle is a simple statement that tries to capture the idea that type assertions should not affect the run-time behaviour of programs. It is usually phrased in terms of adding or removing type assertions to a program on a particular set of inputs. The term was casually coined by the ECMAScript commitee (ECMA TC 39, if memory serves) after Chris Dollin, then working at HP Labs Bristol, argued strongly in favour of its adoption in ECMAScript itself.

Phrased in positive form it states that adding a set of true type assertions to a program will not affect its run-time behaviour for the given inputs. A true type assertion is one that is true for all the given set of inputs. Phrased negatively, it states that removing a set of type assertions cannot change the run-time behaviour on the given set of inputs.