Mercury is a strict, pure functional/logic programming language, with influences from Prolog, ML, and Haskell.
1% Percent sign starts a one-line comment.
2
3 % foo(Bar, Baz)
4 %
5 % Documentation comments are indented before what they describe.
6:- pred foo(bar::in, baz::out) is det.
7
8% All toplevel syntax elements end with a '.' -- a full stop.
9
10% Mercury terminology comes from predicate logic. Very roughly:
11
12% | Mercury | C |
13% | | |
14% | Goal | statement |
15% | expression | expression |
16% | predicate rule | void function |
17% | function rule | function |
18% | head (of a rule) | function name and parameters |
19% | body (of a rule) | function body |
20% | fact | (rule without a body) |
21% | pred/func declaration | function signature |
22% | A, B (conjunction) | A && B |
23% | A ; B (disjunction) | if (A) {} else if (B) {} |
24
25% some facts:
26man(socrates). % "it is a fact that Socrates is a man"
27man(plato).
28man(aristotle).
29
30% a rule:
31mortal(X) :- man(X). % "It is a rule that X is a mortal if X is a man."
32% ^^^^^^-- the body of the rule
33% ^^-- an arrow <--, pointing to the head from the body
34%^^^^^^^^-- the head of the rule
35% this is also a single clause that defines the rule.
36
37% that X is capitalized is how you know it's a variable.
38% that socrates is uncapitalized is how you know it's a term.
39
40% it's an error for 'socrates' to be undefined. It must have a type:
41
42% declarations begin with ':-'
43:- type people
44 ---> socrates
45 ; plato
46 ; aristotle
47 ; hermes.
48 %<--first tab stop (using 4-space tabs)
49 %<--third tab stop (first after --->)
50
51:- pred man(people). % rules and facts also require types
52
53% a rule's modes tell you how it can be used.
54:- mode man(in) is semidet. % man(plato) succeeds. man(hermes) fails.
55:- mode man(out) is multi. % man(X) binds X to one of socrates ; plato ; aristotle
56
57% a semidet predicate is like a test. It doesn't return a value, but
58% it can succeed or fail, triggering backtracking or the other side of
59% a disjunction or conditional.
60
61% 'is semidet' provides the determinism of a mode. Other determinisms:
62% | Can fail? | 0 solutions | 1 | more than 1 |
63% | | | | |
64% | no | erroneous | det | multi |
65% | yes | failure | semidet | nondet |
66
67:- pred mortal(people::in) is semidet. % type/mode in one declaration
68
69% this rule's body consists of two conjunctions: A, B, C
70% this rule is true if A, B, and C are all true.
71% if age(P) returns 16, it fails.
72% if alive(P) fails, it fails.
73:- type voter(people::in) is semidet.
74voter(P) :-
75 alive(P),
76 registered(P, locale(P)),
77 age(P) >= 18. % age/1 is a function; int.>= is a function used as an operator
78
79% "a P is a voter if it is alive, is registered in P's locale, and if
80% P's age is 18 or older."
81
82% the >= used here is provided by the 'int' module, which isn't
83% imported by default. Mercury has a very small 'Prelude' (the
84% 'builtin' module). You even need to import the 'list' module if
85% you're going to use list literals.
Complete runnable example. File in ’types.m’; compile with ‘mmc –make types’.
1:- module types.
2:- interface.
3:- import_module io. % required for io.io types in...
4% main/2 is usually 'det'. threading and exceptions require 'cc_multi'
5:- pred main(io::di, io::uo) is cc_multi. % program entry point
6:- implementation.
7:- import_module int, float, string, list, bool, map, exception.
8
9% enum.
10:- type days
11 ---> sunday
12 ; monday
13 ; tuesday
14 ; wednesday
15 ; thursday
16 ; friday
17 ; saturday.
18
19% discriminated union, like datatype in ML.
20:- type payment_method
21 ---> cash(int)
22 ; credit_card(
23 name :: string, % named fields
24 cc_number :: string,
25 cvv :: int,
26 expiration :: string
27 )
28 ; crypto(coin_type, wallet, amount).
29
30:- type coin_type
31 ---> etherium
32 ; monero. % "other coins are available"
33
34% type aliases.
35:- type wallet == string.
36:- type amount == int.
37
38% !IO is the pair of io.io arguments
39% pass it to anything doing I/O, in order to perform I/O.
40% many otherwise-impure functions can 'attach to the I/O state' by taking !IO
41main(!IO) :-
42 Ints = [
43 3,
44 1 + 1,
45 8 - 1,
46 10 * 2,
47 35 / 5,
48 5 / 2, % truncating division
49 int.div(5, 2), % floored division
50 div(5, 2), % (module is unambiguous due to types)
51 5 `div` 2, % (any binary function can be an operator with ``)
52 7 `mod` 3, % modulo of floored division
53 7 `rem` 3, % remainder of truncating division
54 2 `pow` 4, % 2 to the 4th power
55 (1 + 3) * 2, % parens have their usual meaning
56
57 2 >> 3, % bitwise right shift
58 128 << 3, % bitwise left shift
59 \ 0, % bitwise complement
60 5 /\ 1, % bitwise and
61 5 \/ 1, % bitwise or
62 5 `xor` 3, % bitwise xor
63
64 max_int,
65 min_int,
66
67 5 `min` 3, % ( if 5 > 3 then 3 else 5 )
68 5 `max` 3
69 ],
70 Bools = [
71 yes,
72 no
73 % bools are much less important in Mercury because control flow goes by
74 % semidet goals instead of boolean expressions.
75 ],
76 Strings = [
77 "this is a string",
78 "strings can have "" embedded doublequotes via doubling",
79 "strings support \u4F60\u597D the usual escapes\n",
80 % no implicit concatenation of strings: "concat:" "together"
81 "but you can " ++ " use the string.++ operator",
82
83 % second param is a list(string.poly_type)
84 % s/1 is a function that takes a string and returns a poly_type
85 % i/1 takes an int. f/1 takes a float. c/1 takes a char.
86 string.format("Hello, %d'th %s\n", [i(45), s("World")])
87 ],
88
89 % start with purely functional types like 'map' and 'list'!
90 % arrays and hash tables are available too, but using them
91 % requires knowing a lot more about Mercury
92 get_map1(Map1),
93 get_map2(Map2),
94
95 % list.foldl has *many* variations
96 % this one calls io.print_line(X, !IO) for each X of the list
97 foldl(io.print_line, Ints, !IO),
98 foldl(io.print_line, Bools, !IO),
99 foldl(io.print_line, Strings, !IO),
100 io.print_line(Map1, !IO),
101 % ( if Cond then ThenGoal else ElseGoal )
102 % I/O not allowed in Cond: I/O isn't allowed to fail!
103 ( if Map2^elem(42) = Elem then
104 io.print_line(Elem, !IO)
105 else % always required
106 true % do nothing, successfully (vs. 'fail')
107 ),
108
109 % exception handling:
110 ( try [io(!IO)] ( % io/1 param required or no I/O allowed here
111 io.print_line(received(cash(1234)), !IO),
112 io.print_line(received(crypto(monero, "invalid", 123)), !IO)
113 ) then
114 io.write_string("all payments accepted\n", !IO) % never reached
115 catch "monero not yet supported" -> % extremely specific catch!
116 io.write_string("monero payment failed\n", !IO)
117 ).
118
119:- pred get_map1(map(string, int)::out) is det.
120get_map1(!:Map) :- % !:Map in the head is the final (free, unbound) Map
121 !:Map = init, % !:Map in the body is the next Map
122 det_insert("hello", 1, !Map), % pair of Map vars
123 det_insert("world", 2, !Map),
124
125 % debug print of current (bound) Map
126 % other [Params] can make it optional per runtime or compiletime flags
127 trace [io(!IO)] (io.print_line(!.Map, !IO)),
128
129 det_insert_from_corresponding_lists(K, V, !Map),
130 % this code is reordered so that K and V and defined prior to their use
131 K = ["more", "words", "here"],
132 V = [3, 4, 5].
133
134:- pred get_map2(map(int, bool)::out) is det.
135get_map2(Map) :-
136 det_insert(42, yes, map.init, Map).
137
138:- func received(payment_method) = string.
139received(cash(N)) = string.format("received %d dollars", [i(N)]).
140received(credit_card(_, _, _, _)) = "received credit card". % _ is throwaway
141received(crypto(Type, _Wallet, Amount)) = S :- % _Wallet is named throwaway
142 ( % case/switch structure
143 Type = etherium,
144 S = string.format("receiving %d ETH", [i(Amount)])
145 ;
146 Type = monero,
147 throw("monero not yet supported") % exception with string as payload
148 ).
That was quick! Want more? ¶
More Tutorials ¶
- Mercury Tutorial (pdf link) - a more traditional tutorial with a more relaxed pace
- Mercury Crash Course - a dense example-driven tutorial with Q&A format
- GitHub Wiki Tutorial
- Getting Started with Mercury - installation and your first steps
Documentation ¶
- Language manual, user’s guide, and library reference are all at mercurylang.org