Timber has a rich expression language and we divide the description in three parts:
Here we describe the Timber form of expression forms that have counterparts in most functional languages.
These forms are special to Timber and provide the building blocks for object-oriented programming.
Every Timber program will include expressions from the previous two groups. In this third group we describe more esoteric forms that may be ignored by the beginning Timber programmer.
Layout of program code is significant in Timber. In many cases, sequences of syntactic elements can be written in tabular form, where each new item starts on a new line and in the same column as the beginning of the previous item. Multi-line items are possible by intending subsequent lines; the end of the sequence is signalled by "outdenting", i.e. indenting less, as in the example
showSeq xs = concat (map showLine xss) where prec = 100 showLine x = map (showItem 10) (comp prec xx) showItem n x = format n ('_' : g x)
The definition of showSeq has a where-clause with a list of two local definitions (defining prec and showLine). The definition of showLine spans two lines, indicated by indentation. The last definition, of showItem, has another indentation level, thus is not part of the where-clause and is hence not local to showSeq. Instead showSeq and showItem form another list of bindings.
It is possible to avoid layout-sensitive syntax by enclosing such sequences in braces and using semi-colon as separator: let {x=1; y=f x 3} in g x y; this is generally not recommended.
Examples |
---|
x, counter, Dictionary.d, (+) |
Type of literal | Examples |
---|---|
Int | 37, 0, -123 |
Float | 1.23, 3E12, 0.4567E-6 |
Char | 'a', '3', '\n', '\t', '\\' |
String | "Hi", "Error\n" |
Every occurrence of an integer literal except in patterns is during desugaring replaced by application of the function fromInt to the literal. The Prelude defines
typeclass IntLiteral a where fromInt :: Int -> aand instances for Int (the identity function) and Float (conversion).
This device will make many functions that mention integer literals usable for both Int and Float arguments; a simple example is
sum :: [a] -> a \\ Num a, IntLiteral a sum [] = 0 sum (x : xs) = x + sum xs
Examples | Comments |
---|---|
f 3 | Parentheses not needed |
g (x+2) | Parentheses necessary |
Just (f x) | Constructor Just applied to f x |
map f (g xs) | map f applied to g xs |
hMirror (x,y) | One argument, which is a pair |
Examples | Desugared forms |
---|---|
x+3 | (+) x 3 |
f x+3*x | (+) (f x) ((*) 3 x) |
3 `elem` xs | elem 3 xs |
Precedence and associativity of operators is determined syntactically; see the lexical structure page for details.
\pat+ -> expr
denoting anonymous functions. The sequence of patterns must be linear, i.e. may not include more than one occurrence of any variable.
Example |
---|
\x -> x+3 |
\ (Just x) d -> insert x 0 d |
Pairs, triples etc are expressed using parenheses as delimiters and comma as separator. These are also desugered to application:
Examples | Desugared forms |
---|---|
(a,b) | (,) a b |
(f x,3+5,True) | (,,) (f x) ((+) 3 5) True |
Here (,), (,,) etc are primitive constructors for the types of pairs, triples etc.
List expressions are sequences delimited by square brackets and with comma as separators;[1,3,7] is a list with three elements. This form is desugared to the primitive form of lists, which has as constructors [] (the empty list) and : ("cons", which builds a list with its first argument as first element ("head") and second argument as remainder ("tail")).
Examples | Desugared forms |
---|---|
[1,3,7] | (:) 1 ((:) 3 ((:) 7 [])) |
[f x, p 3] | (:) (f x) ((:) (p 3) []) |
let bind+ in expr
where the list of bindings is layout-sensitive. The bindings are recursive, i.e all the names defined in the bind list are in scope in the right hand sides of these bindings (and, of course, in the main expression).
Example | Comments |
---|---|
let size = f 100 | f, defined below, is in scope |
f 0 = 0 | Pattern-matching allowed |
f x = g x (x+2) | |
in concat (map f xs) |
[ type_constructor ] { field (, field )+ }where field has the form
selector = exprThe type_constructor is the name of a struct type. Since the struct type is uniquely determined by its fields, the type name is optional.
Examples |
---|
Point {x=3, y=1} |
{x=3, y=1} |
Counter {inc = inc, read = read} |
The examples presupposes type definitions
struct Point where x, y :: Int struct Counter where inc :: Action read :: Request Int
The last example is to emphasise that selectors (the inc and read in the left hand sides of the fields) are in a separate namespace from variables (the right hand sides). Thus the equations are not recursive; the example requires that definitions of these variables are in scope.
type_constructor {[ field (, field )+] ..}
The trailing .. indicates that the struct value should be stuffed, by adding fields of the form x = x for each selector x that is not explicitly given a value in a field.
Example | expands to |
---|---|
Counter {..} | Counter {inc = inc, read = read} |
Counter {read = readreq ..} | Counter {read = readreq, inc = inc} |
Point {x=0..} | Point {x=0, y=y} |
Obviously, because of subtyping, the type name cannot be omitted here.
struct bind+
where the bindings have layout-sensitive syntax.
Example |
---|
instance showList :: Show [a] \\ Show a |
showList = struct |
show [] = "[]" |
show (x : xs) = '[':show x ++ preC (map show xs)++"]" |
The list of bindings in this form of expression differs from all other occurrences of lists of bindings in that they are not recursive. We are defining the selectors in a struct but allow pattern matching equations in layout-sensitive form; occurrence of the same name as a variable in the right hand side must refer to some other definition in scope. This is particularly common for structs defined to be type classes, as here. See the section on type classes for more explanatation.
case expr of alt+
where the sequence of alternatives is layout-sensitive. The simplest form of an alternative is
pat -> expr
Example |
---|
case f a b of |
[] -> 0 |
x : xs -> g x + h 3 xs |
Classes in Timber should be thought of as in object-oriented programming: a class is a template, from which we may create several objects. An object encapsulates its own copy of the state variables defined in the class.
Actions and requests are methods that a class may expose to the outside; they may manipulate the local state of objects.
Procedures are subroutines that may be called by actions and requests; they are typically not exposed in interfaces. In contrast to actions and requests, a procedure always reads and writes the local state of its caller, irrespective of the scope in which it was defined.
The syntax of these constructs are similar; they are all built from sequences of statements:
In all cases, statement sequences have layout-sensitive syntax. See the statements page
for further descriptions.
Here we just summarize for the Haskell programmer expression forms that are available also in Timber and with the same syntax:
after expr expr
before expr expr
Here the first expr must be a Time and the second an action;
the latter is given a new baseline (the after case) or deadline (the before case).
Further forms of expressions