2.1 Core Syntactic Forms
2.1.1 Type Annotation
syntax
(: expr type)
> (: 42 Integer)
: Integer
42
> (: "hello" String)
: String
"hello"
> (: "hello" Integer) eval:4:0: typechecker: type mismatch
between: String
and: Integer
in: "hello"
Additionally, some forms (such as def and defn) recognize literal uses of : to provide additional locations for type annotations.
2.1.2 Definitions
syntax
(def id maybe-type maybe-fixity-ann val-expr)
maybe-type = : type |
maybe-fixity-ann = #:fixity fixity |
fixity = left | right
> (def x 7) > x
: Integer
7
> (def y : Integer 7) > (def z : String 7) eval:6:0: typechecker: type mismatch
between: Integer
and: String
in: 7
If fixity is specified, it defines id’s operator fixity when used as an infix operator.
syntax
(defn id maybe-type maybe-fixity-ann [[arg-pat ...+] body-expr] ...+)
maybe-type = : type |
maybe-fixity-ann = #:fixity fixity |
fixity = left | right
(def id maybe-type (lambda [arg ...] (case* [arg ...] [[arg-pat ...] body-expr] ...)))
The defn form is generally preferred when defining top-level functions.
2.1.3 Anonymous Functions
(lambda [arg ...] (case* [arg ...] [[arg-pat ...] body-expr] ...))
2.1.4 Local Bindings
> (let ([x 10]) x)
: Integer
10
> (let ([x 10] [y (+ x 1)]) y)
: Integer
11
> (let ([x 10] [y {x + 1}] [z {y * 2}]) z)
: Integer
22
This allows val-exprs to refer to themselves (to create circular values or recursive functions) or other bindings (to create shared or mututally recursive values or functions).
> (letrec ([xs {1 :: xs}]) (take 5 xs))
: (List Integer)
{1 :: 1 :: 1 :: 1 :: 1 :: Nil}
> (letrec ([xs {1 :: ys}] [ys {2 :: xs}]) (take 5 xs))
: (List Integer)
{1 :: 2 :: 1 :: 2 :: 1 :: Nil}
2.1.5 Pattern Matching
syntax
(case val-expr [pat body-expr] ...+)
syntax
(case* [val-expr ...+] [[pat ...+] body-expr] ...+)
> (case* [(Just 1) (Just 2)] [[(Just _) Nothing] "first"] [[Nothing (Just _)] "second"] [[(Just _) (Just _)] "both"] [[Nothing Nothing] "neither"])
: String
"both"
syntax
(pattern (name-id param-id ...) body-pattern/expr)
The body-pattern/expr form will be interpreted in two different ways—as a pattern or as an expression—depending on the context within which it is used. The pattern (name-id arg-id ...) will consistently match the value produced by (name-id arg-expr ...), assuming every pattern used in body-pattern/expr also has this property.
> (data (Exp* e) (Var* String) (App* e e) (Lam* String e) #:deriving [Show])
> (data Exp (E (Exp* Exp)) #:deriving [Show]) > (pattern (Var x) (E (Var* x))) > (pattern (App a b) (E (App* a b))) > (pattern (Lam x a) (E (Lam* x a)))
> (defn free : {Exp -> (List String)} [[(Var x)] (List x)] [[(App f a)] {(free f) ++ (free a)}] [[(Lam x b)] (filter (/= x) (free b))]) > (free (Var "x"))
: (List String)
{"x" :: Nil}
> (free (App (Lam "x" (App (Var "x") (Var "y"))) (Lam "z" (App (Var "a") (Var "z")))))
: (List String)
{"y" :: "a" :: Nil}
2.1.6 Imports
syntax
(require require-spec ...)