Primer is a dynamically typed programming language with a mixture of imperative and functional facilities. If you have any comments or questions, please feel free to email me.
An alpha version will be released shortly for Linux, Mac and Windows. The source code will be available under the MIT license.
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
If you receive the error message "Unable to find standard library..." then you may need to restart your shell for the environment variable to be exported correctly.
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
A binding can be reassigned using set.
def x = 12 set x = true
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)
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. Functions can be nested to an arbitrary depth and are lexically scoped. They return the value of their last executed expression and can be called recursively. 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 provides a few built in statements. Both if statements and while loops work with boolean predicates or integer expressions. The if statement can have an optional else clause and can be nested to any depth.
while (b == true)
if (x > 3000)
set x = x - 10
else
set b = false
end
end
The iter statement loops over the elements of a list. Note that if the list is modified in the loop body, the effect will not be visible until the loop has terminated.
iter (e : [1,2,3])
show(e)
end
The let block creates a new lexical environment, bringing bindings into scope for the extent of its body.
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
Note that fn, if, for and while also introduce their own lexical environments. Because Primer is lexically scoped, bindings can be shadowed.
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)
String 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 % 2
The standard logical operators are also available (not all are shown).
a == 15 && c != 12 || a <= 6
The range operator creates a list of integers between lower and upper bounds (inclusive).
50..300
The standard library, found in stdlib.pri, is a small collection of functions that are automatically loaded by the interpreter before it evaluates your program. All of these functions are written in Primer itself so you are free to change the implementation of them as you see fit (for example, some are written in a deliberately functional style to keep them short and elegant but could be rewritten imperatively to improve efficiency).
Note that a small number of functions are actually special forms: cons, head, tail, type and show.
As Primer makes heavy use of lists, many of the standard library functions relate to manipulating them. The following describes some of the most interesting functions but other facilities include searching, sorting and summing.
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.
def square = fn (x)
x * x
end
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]) foldr(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])