diff options
Diffstat (limited to 'mercury.html.markdown')
| -rw-r--r-- | mercury.html.markdown | 263 | 
1 files changed, 263 insertions, 0 deletions
| diff --git a/mercury.html.markdown b/mercury.html.markdown new file mode 100644 index 00000000..76c7d1ca --- /dev/null +++ b/mercury.html.markdown @@ -0,0 +1,263 @@ +--- +language: mercury +contributors: +    - ["Julian Fondren", "https://mercury-in.space/"] +--- + +Mercury is a strict, pure functional/logic programming language, with +influences from Prolog, ML, and Haskell. + +```mercury +% Percent sign starts a one-line comment. + +    % foo(Bar, Baz) +    % +    % Documentation comments are indented before what they describe. +:- pred foo(bar::in, baz::out) is det. + +% All toplevel syntax elements end with a '.' -- a full stop. + +% Mercury terminology comes from predicate logic. Very roughly: + +% | Mercury               | C                            | +% |                       |                              | +% | Goal                  | statement                    | +% | expression            | expression                   | +% | predicate rule        | void function                | +% | function rule         | function                     | +% | head (of a rule)      | function name and parameters | +% | body (of a rule)      | function body                | +% | fact                  | (rule without a body)        | +% | pred/func declaration | function signature           | +% | A, B  (conjunction)   | A && B                       | +% | A ; B (disjunction)   | if (A) {} else if (B) {}     | + +% some facts: +man(socrates).  % "it is a fact that Socrates is a man" +man(plato). +man(aristotle). + +% a rule: +mortal(X) :- man(X).  % "It is a rule that X is a mortal if X is a man." +%            ^^^^^^-- the body of the rule +%         ^^-- an arrow <--, pointing to the head from the body +%^^^^^^^^-- the head of the rule +% this is also a single clause that defines the rule. + +% that X is capitalized is how you know it's a variable. +% that socrates is uncapitalized is how you know it's a term. + +% it's an error for 'socrates' to be undefined. It must have a type: + +% declarations begin with ':-' +:- type people +    --->    socrates +    ;       plato +    ;       aristotle +    ;       hermes. +    %<--first tab stop (using 4-space tabs) +            %<--third tab stop (first after --->) + +:- pred man(people).  % rules and facts also require types + +% a rule's modes tell you how it can be used. +:- mode man(in) is semidet.  % man(plato) succeeds. man(hermes) fails. +:- mode man(out) is multi.   % man(X) binds X to one of socrates ; plato ; aristotle + +% a semidet predicate is like a test. It doesn't return a value, but +% it can succeed or fail, triggering backtracking or the other side of +% a disjunction or conditional. + +% 'is semidet' provides the determinism of a mode. Other determinisms: +% | Can fail? | 0 solutions | 1       | more than 1 | +% |           |             |         |             | +% | no        | erroneous   | det     | multi       | +% | yes       | failure     | semidet | nondet      | + +:- pred mortal(people::in) is semidet.  % type/mode in one declaration + +% this rule's body consists of two conjunctions: A, B, C +% this rule is true if A, B, and C are all true. +% if age(P) returns 16, it fails. +% if alive(P) fails, it fails. +:- type voter(people::in) is semidet. +voter(P) :- +    alive(P), +    registered(P, locale(P)), +    age(P) >= 18.  % age/1 is a function; int.>= is a function used as an operator + +% "a P is a voter if it is alive, is registered in P's locale, and if +% P's age is 18 or older." + +% the >= used here is provided by the 'int' module, which isn't +% imported by default. Mercury has a very small 'Prelude' (the +% 'builtin' module). You even need to import the 'list' module if +% you're going to use list literals. +``` + +Complete runnable example. File in 'types.m'; compile with 'mmc --make types'. + +```mercury +:- module types. +:- interface. +:- import_module io.  % required for io.io types in... +% main/2 is usually 'det'. threading and exceptions require 'cc_multi' +:- pred main(io::di, io::uo) is cc_multi.  % program entry point +:- implementation. +:- import_module int, float, string, list, bool, map, exception. + +% enum. +:- type days +    --->    sunday +    ;       monday +    ;       tuesday +    ;       wednesday +    ;       thursday +    ;       friday +    ;       saturday. + +% discriminated union, like datatype in ML. +:- type payment_method +    --->    cash(int) +    ;       credit_card( +                name :: string,         % named fields +                cc_number :: string, +                cvv :: int, +                expiration :: string +            ) +    ;       crypto(coin_type, wallet, amount). + +:- type coin_type +    --->    etherium +    ;       monero.  % "other coins are available" + +% type aliases. +:- type wallet == string. +:- type amount == int. + +% !IO is the pair of io.io arguments +% pass it to anything doing I/O, in order to perform I/O. +% many otherwise-impure functions can 'attach to the I/O state' by taking !IO +main(!IO) :- +    Ints = [ +        3, +        1 + 1, +        8 - 1, +        10 * 2, +        35 / 5, +        5 / 2,      % truncating division +        int.div(5, 2),  % floored division +        div(5, 2),  % (module is unambiguous due to types) +        5 `div` 2,  % (any binary function can be an operator with ``) +        7 `mod` 3,  % modulo of floored division +        7 `rem` 3,  % remainder of truncating division +        2 `pow` 4,  % 2 to the 4th power +        (1 + 3) * 2,    % parens have their usual meaning + +        2 >> 3,     % bitwise right shift +        128 << 3,   % bitwise left shift +        \ 0,        % bitwise complement +        5 /\ 1,     % bitwise and +        5 \/ 1,     % bitwise or +        5 `xor` 3,  % bitwise xor + +        max_int, +        min_int, + +        5 `min` 3,  % ( if 5 > 3 then 3 else 5 ) +        5 `max` 3 +    ], +    Bools = [ +        yes, +        no +        % bools are much less important in Mercury because control flow goes by +        % semidet goals instead of boolean expressions. +    ], +    Strings = [ +        "this is a string", +        "strings can have "" embedded doublequotes via doubling", +        "strings support \u4F60\u597D the usual escapes\n", +        % no implicit concatenation of strings: "concat:" "together" +        "but you can " ++ " use the string.++ operator", + +        % second param is a list(string.poly_type) +        % s/1 is a function that takes a string and returns a poly_type +        % i/1 takes an int. f/1 takes a float. c/1 takes a char. +        string.format("Hello, %d'th %s\n", [i(45), s("World")]) +    ], + +    % start with purely functional types like 'map' and 'list'! +    % arrays and hash tables are available too, but using them +    % requires knowing a lot more about Mercury +    get_map1(Map1), +    get_map2(Map2), + +    % list.foldl has *many* variations +    % this one calls io.print_line(X, !IO) for each X of the list +    foldl(io.print_line, Ints, !IO), +    foldl(io.print_line, Bools, !IO), +    foldl(io.print_line, Strings, !IO), +    io.print_line(Map1, !IO), +    % ( if Cond then ThenGoal else ElseGoal ) +    % I/O not allowed in Cond: I/O isn't allowed to fail! +    ( if Map2^elem(42) = Elem then +        io.print_line(Elem, !IO) +    else % always required +        true  % do nothing, successfully (vs. 'fail') +    ), + +    % exception handling: +    ( try [io(!IO)] ( % io/1 param required or no I/O allowed here +        io.print_line(received(cash(1234)), !IO), +        io.print_line(received(crypto(monero, "invalid", 123)), !IO) +    ) then +        io.write_string("all payments accepted\n", !IO) % never reached +    catch "monero not yet supported" -> % extremely specific catch! +        io.write_string("monero payment failed\n", !IO) +    ). + +:- pred get_map1(map(string, int)::out) is det. +get_map1(!:Map) :-  % !:Map in the head is the final (free, unbound) Map +    !:Map = init,   % !:Map in the body is the next Map +    det_insert("hello", 1, !Map),  % pair of Map vars +    det_insert("world", 2, !Map), + +    % debug print of current (bound) Map +    % other [Params] can make it optional per runtime or compiletime flags +    trace [io(!IO)] (io.print_line(!.Map, !IO)), + +    det_insert_from_corresponding_lists(K, V, !Map), +    % this code is reordered so that K and V and defined prior to their use +    K = ["more", "words", "here"], +    V = [3, 4, 5]. + +:- pred get_map2(map(int, bool)::out) is det. +get_map2(Map) :- +    det_insert(42, yes, map.init, Map). + +:- func received(payment_method) = string. +received(cash(N)) = string.format("received %d dollars", [i(N)]). +received(credit_card(_, _, _, _)) = "received credit card".  % _ is throwaway +received(crypto(Type, _Wallet, Amount)) = S :-  % _Wallet is named throwaway +    ( % case/switch structure +        Type = etherium, +        S = string.format("receiving %d ETH", [i(Amount)]) +    ; +        Type = monero, +        throw("monero not yet supported")  % exception with string as payload +    ). +``` + +## That was quick! Want more? + +### More Tutorials + +* [Mercury Tutorial](https://mercurylang.org/documentation/papers/book.pdf) (pdf link) - a more traditional tutorial with a more relaxed pace +* [Mercury Crash Course](https://mercury-in.space/crash.html) - a dense example-driven tutorial with Q&A format +* [Github Wiki Tutorial](https://github.com/Mercury-Language/mercury/wiki/Tutorial) +* [Getting Started with Mercury](https://bluishcoder.co.nz/2019/06/23/getting-started-with-mercury.html) - installation and your first steps +   +### Documentation + +* Language manual, user's guide, and library reference are all at +  [mercurylang.org](https://mercurylang.org/documentation/documentation.html) | 
