Primer is a simple functional programming language in the early stages of development. An alpha version will be released shortly under the MIT license. If you have any comments or questions, please feel free to email me.
Unpack the tarball and ensure that the Primer binary is somewhere on your PATH. Create the environment variable PRIMER_LIBRARY_PATH and set it to the path where stdlib.pri will live on your system. To test that everything's working, enter the following code into a text file and save it as hello.pri.
show("Hello, world!")
At the shell enter:
$ primer hello.pri
Definitions, created with the def keyword, intern a new symbol which can be bound to a value, an expression or a function definition. Values can be integers, floating point numbers, booleans, characters or strings as well as the value nil which represents nothing.
def i = 10 def f = 3.14 def b = true def c = 'z' def s = "Hello, world!"
You can create multiple definitions at a time.
def foo = 12, bar = true, baz = 1.112
Functions are introduced with the fn keyword. They are first class objects so can be bound to a symbol, passed to another function or stored in a list. They can also be passed anonymously. Functions can be nested to an arbitrary depth and are lexically scoped. They return the value of their last executed expression. Functions can be called recursively and Primer will optimise tail calls. The following example shows a higher order function being passed both a named and an anonymous function.
def square = fn(x)
x * x
end
def addfuns = fn(f1, f2, x)
f1(x) + f2(x)
end
addfuns(square, fn (x) x + x end, 5)
Primer is lexically scoped - functions and if statements create a new lexical environment. Additionally, the let statement creates a new lexical environment which brings bindings into scope for the extent of its body. The following example shows simple shadowing of bindings.
let (w = [1,2,3])
show(w)
let (w = nil, x = 12, y = 4, z = true)
show([x + y] ++ [z, w])
end
show(w)
end
Lists are heterogeneous and can be nested to an arbitrary depth. They can be concatenated with the ++ operator. List equality is tested element-wise and nested lists are tested recursively. The empty list is equal to nil.
def simple = [1,2,3] def nested = [1,2,[12,15,16,[44],67],3,3,4,2] def mixed = [1, true, 11128.775, square, 'Y', nil, "foo"] def new = simple ++ nested ++ mixed ++ [x, y]
An atomic item can be prepended to a list with cons.
def extended = cons(0, [1,2,3])
A number of functions provide access to list elements such as head, tail, last and nth.
head([1,2,3]) tail([1,2,3]) last([1,2,3]) nth([1,2,3], 0)
Strings are lists of characters with additional syntactic sugar meaning that many list operations such as ++, length, reverse and sort can be used with them.
def name = "John " ++ "Smith" show(length(name)) set name = reverse(sort(name))
The standard mathematical operators have precedence as in C.
12 + 3 * 2 / 5 mod 2
The standard logical operators are also available (not all are shown).
a == 15 and c != 12 or a <= 6
The range operator creates a list of integers between lower and upper bounds (inclusive).
50..300
The if statement has an optional else clause and can be nested to any depth.
if (x > 3000)
show("large")
else
show("small")
end
The map function transforms the values of a list by applying a function to each member in turn and returning a new list containing the results. The supplied function should take one argument and return one value.
map(square, [1,2,3])
The filter function applies a predicate to each member of a list, returning a new list consisting of those elements for which the predicate returns true. The predicate should be a function taking one argument and returning a boolean.
filter(even, [1,2,3,4,5])
Fold, sometimes known as 'reduce', is implemented as two higher order functions, foldl and foldr. It processes a list by combining elements using a binary function and returns a result. The foldl function (left fold) recursively combines the results of combining all but the last element of the list with the last one. The foldr function (right fold) recursively combines the first element of the list with the results of combining the tail of the list. An initial value is used for combining the elements at the end of the list.
foldl(fn (x, y) x + y end, 0, [1,2,3,4,5])
The zip function combines elements from two lists, pairwise, into a new list. If the lists are of unequal length, the resultant list will contain as many elements as the shortest list.
zip([1,2,3], [4,5,6])
A number of functions are provided by the standard library for determining the type of a variable. The type function returns an integer code while the type predicate functions return a boolean value indicating if the supplied object is of that type.
type(i) isint(i) isfloat(f) isbool(b) ischar(c) isfn(square)