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 +. xThe 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 intValues 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!"The 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 * chainRecursive functions need a rec keyword, as in OCaml.
let rec str_of_chain = function
| Chain_end -> "end"Concatenation is done using the (^) function.
| Chain_add (x, rest) ->
x ^ " + " ^ str_of_chain restTypes can have parameters. These stand for another type, and can be inferred.
type li 'a =
| Nil_
| Cons_ of 'a * li 'aval map : ('a -> 'b) -> li 'a -> li 'blet rec map f = function
| Nil_ -> Nil_
| Cons_ (x, xs) -> Cons_ (f x, map f xs)Amulet has a built-in type of lists, list 'a.
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 + cType 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"
endMethods can be used with any type that has a corresponding instance.
let "int" = to_str 123
let "bool" = to_str trueType class methods can be polymorphic in their return types.
type a_box 'a = Box of 'aThe class 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
endAmulet selects the instance to use automatically, based on the context into is used in.
let Box 123 = into 123
let [123] = into 123into_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 pure and <*>. These definitions of cartesian are identical.
let cartesian (xs : list _) ys =
pure (,) <*> xs <*> ysAmulet has special syntax for use of the monad class.
let might_fail x =
if x >= 10 then
None
else
Some xThe monad option instance aborts the computation when it encounters a None.
let None =
let! x = might_fail 1
let! y = might_fail 11
pure (x + y)Computations without None are simply chained.
let Some 2 =
let! x = might_fail 1
let! y = might_fail 1
pure (x + y)