This is part of a series of examples of the Amulet programming language. If you do not yet have an Amulet compiler, please refer to the installation instructions.
open import "foo.ml" imports
foo from the library path.
open import "prelude.ml"
Let bindings introduce variables. These variables can not be mutated.
let foo = 1 let bar = ""
Type inference can figure out the type of a variable without the programmer having to supply it.
let foo x = x + x
(+) operates on ints,
(+.) on floats.
let bar x = x +. x
type construct can be used to define a new data type. Types are defined by listing their constructors
type foo = | Foo_str of string | Foo_int of int
Values of algebraic data types can be taken apart with pattern matching.
let get_foo_str x = match x with | Foo_str x -> x | Foo_int _ -> "it wasn't a string!"
function keyword abbreviates a pattern matching function.
let get_foo_str' = function | Foo_str x -> x | Foo_int _ -> "not a string again?"
Pattern matching on
bool can be done with
if .. then .. else ...
let is_gte x y = if x >= y then "it is" else "it's not"
User-defined data types can be recursive.
type chain = | Chain_end | Chain_add of string * chain
Recursive functions need a
rec keyword, as in OCaml.
let rec str_of_chain = function | Chain_end -> "end"
Concatenation is done using the
| Chain_add (x, rest) -> x ^ " + " ^ str_of_chain rest
Types can have parameters. These stand for another type, and can be inferred.
type li 'a = | Nil_ | Cons_ of 'a * li 'a
val map : ('a -> 'b) -> li 'a -> li 'b
let rec map f = function | Nil_ -> Nil_ | Cons_ (x, xs) -> Cons_ (f x, map f xs)
Amulet has a built-in type of lists,
let foo : list int = [1, 2, 3, 4, 5]
Lists can be manipulated using list comprehensions.
let greater_than_3 = [ x | with x <- foo , x > 3 ]
List literal syntax can also be used in patterns.
let [a, b, c] = [1, 2, 3] let _ = a + b + c
Type classes are used for principled overloading.
class to_string 'a begin val to_str : 'a -> string end instance to_string int begin let to_str _ = "int" end instance to_string bool begin let to_str _ = "bool" end
Methods can be used with any type that has a corresponding
let "int" = to_str 123 let "bool" = to_str true
Type class methods can be polymorphic in their return types.
type a_box 'a = Box of 'a
into_box is parametrised by a type constructor.
class into_box 'f begin val into : 'a -> 'f 'a end instance into_box list begin let into x = [x] end instance into_box a_box begin let into = Box end
Amulet selects the instance to use automatically, based on the context
into is used in.
let Box 123 = into 123 let  = into 123
into_box is almost the standard class
applicative. Amulet has special syntax for using
applicative functors, idiom brackets:
let cartesian (xs : list _) ys = | (,) xs ys |) (
Idiom brackets desugar into uses of
<*>. These definitions of
cartesian are identical.
let cartesian (xs : list _) ys = pure (,) <*> xs <*> ys
Amulet has special syntax for use of the
let might_fail x = if x >= 10 then None else Some x
monad option instance aborts the computation when it encounters a
let None = let! x = might_fail 1 let! y = might_fail 11 pure (x + y)
None are simply chained.
let Some 2 = let! x = might_fail 1 let! y = might_fail 1 pure (x + y)