Mercury
- File size
- 10.7KB
- Lines of code
- 257
Mercury
Functional language for complex algorithm implementation and formal verification.
Comments
% ----- COMMENT -----
% this is a single-line comment
/* this is a
multi-line
comment */
Printing
% ----- PRINTING -----
% io.write_string() => receives a string argument that is then printed to the stdout and does not include a newline by default
% !IO => provided as the second argument to the io.write_string() function
% note there is no built-in implementation for printing to the stdout with a newline included automatically
io.write_string("this does not have a newline", !IO).
io.write_string("this includes a newline but only because we explicitly specify its inclusion\n", !IO).
Quickstart
% ----- QUICKSTART -----
% period-delimited programming language
% strongly, statically typed
% encourages declarative programming with a sophisticated determinism system
% supports concurrency and automatic garbage collection
% main(!IO) => specifies the entry point of a Mercury program wherein all execution code is written, the equivalent of the main function in many other programming languages within the C family
% import_module => brings other user-defined and Mercury built-in modules into the current module's local scope
% module => declares the beginning of a module
% :- => general-purpose syntax used for variable declaration, directives, and clauses
Types
% ----- TYPE -----
% int => stores signed and unsigned integer number values
% float => stores signed and unsigned floating-point number values
% char => stores a single Unicode character value, declared within '' single quotation marks
% bool => true, false
% string => stores a string value, declared within "" double quotation marks
:- type int.
:- type float.
:- type char.
:- type bool.
:- type string.
Operators
% ----- OPERATOR -----
% --- ARITHMETIC OPERATOR ---
+ % addition
- % subtraction
* % multiplication
/ % division
% % modulo
% --- COMPARISON OPERATOR ---
= % partial equality check for value but not type
\= % partial inequality check for value but not type
> % comparison operator
< % comparison operator
>= % comparison operator
<= % comparison operator
% --- LOGICAL OPERATOR ---
, % conjunction operator, used as logical and
; % disjunction operator, used as logical or
not % negation operator, used as logical not
Control structures
% ----- CONTROL STRUCTURE -----
% --- CONDITIONALS ---
% IF ELSE IF ELSE
:- pred check_age(int::in) is semidet.
check_age(Age) :-
( if Age >= 18 then
io.write_string("Adult\n")
else if Age >= 13 then
io.write_string("Teenager\n")
else
io.write_string("Child\n") ).
% CASE = ->
% provides basic pattern-matching similar to Rust and other programming languages
:- type color ---> red ; blue ; green.
:- pred describe_color(color::in, io::di, io::uo) is det.
describe_color(Color, !IO) :-
( Color = red ->
io.write_string("Color is Red\n", !IO)
; Color = blue ->
io.write_string("Color is Blue\n", !IO)
; Color = green ->
io.write_string("Color is Green\n", !IO)
).
% --- LOOPS ---
% Mercury lacks coventional for and while loop constructs as in other programming languages
% instead, recursion can be used as seen below to iterate over and traverse an iterable data structure similar to Clojure and other Lisp dialects in a manner similar to for loops
% while loops can also be effected by including a recursive construct and adding a conditional predicate check that breaks out of the loop when the predicate condition is met
% RECURSION TO ITERATE OVER ITERABLE STRUCTURE
:- pred sum_list(list(int)::in, int::out, io::di, io::uo) is det.
sum_list(List, Sum, !IO) :-
sum_list_loop(List, 0, Sum, !IO).
:- pred sum_list_loop(list(int)::in, int::in, int::out, io::di, io::uo) is det.
sum_list_loop([], Acc, Acc, !IO).
sum_list_loop([Head | Tail], Acc, Sum, !IO) :-
NewAcc = Acc + Head,
sum_list_loop(Tail, NewAcc, Sum, !IO).
% RECURSION WITH EXIT PREDICATE CONDITION
:- pred count_down(int::in, io::di, io::uo) is det.
count_down(N, !IO) :-
( if N > 0 then
io.write_string(string.format("Counting down: %d\n", [i(N)]), !IO),
count_down(N - 1, !IO)
else
io.write_string("Countdown complete!\n", !IO)
).
Data structures
% ----- DATA STRUCTURE -----
% list => dynamically-sized ordered collection of elements of the same datatype
% array => fixed-size ordered collection of elements of the same datatype
% type => used to specify one of the following user-defined datatypes
% user-defined set of named constants, the equivalent of enums in other languages
% user-defined collection of named fields and their specified corresponding datatypes, the equivalent of structs in Rust and Go allowing for the modelling of structured data
% option() => creates a nullable datatype that can either store a value of the specified datatype as some() or the special value none
% some() => represents the presence of a value stored in a variable that is nullable and could be storing the none value
% none => represents the absence of a value
% alias => special keyword that creates a type alias for existing datatypes, from which that aliased datatype can then be called
:- type list(T).
:- type array(T, N).
:- type color. % enum equivalent
:- type person. % struct equivalent
:- type option(T).
:- type alias Name == string. % alias Name for the existing datatype string
Functions and Predicates
% ----- FUNCTION -----
% used for computation and deterministic, producing a single output for a given number of inputs
% Mercury function definitions bear many similarities to functional languages like Haskell, where the function's type signature is specified first and function implementation within the function body is specified after
% bearing hallmarks of the functional paradigm, Mercury's functions feature implicit return of the last expression within the function definition, and each function returns a single value
% func <functionName> ( <functionParameterDatatype(s)> ) = <functionReturnDatatype> . <functionName> ( <functionParameterName(s)> ) <functionBody> => function declaration and definition of type signature of a named function
:- func add(int, int) = int. % named function's type signature
add(X, Y) = X + Y. % named function's function body
:- func multiply(int, int) = int. % another named function's type signature
multiply(X, Y) = X * Y. % named function's function body
:- func factorial(int) = int. % a final named function's type signature
factorial(N) = ( if N =< 0 then 1 else N * factorial(N - 1) ). % that named function's function body
% ----- PREDICATE -----
% used for logical assertions that can succeed or fail, with the following determinism categories such as the following
% det => always succeeds exactly once
% semidet => succeeds at most once and may fail
% nondet => can succeed multiple times
% multi => must succeed at least once, but can succeed multiple times
% pred <predicateName> ( <predicateParameterDatatype(s)> :: <predicateModeParameterDescriptions>) is <determinismCategory> . <predicateName> ( <predicateParameterName(s)> ) :- <predicateBody> => predicate declaration and definition for a named predicate
% predicate mode parameter descriptions are used to specify how parameters are passed to and from predicates
% in => the predicate parameter will be read by the predicate but will not be modified by it
% di => the predicate parameter will be consumed (destroyed) by the predicate
% uo => the predicate parameter will be used to produce an output returned value
:- pred is_even(int::in) is semidet. % type signature for a predicate
is_even(N) :- N mod 2 = 0. % predicate implementation
:- pred check_age(int::in, io::di, io::uo) is det. % type signature for a more complex predicate
check_age(Age, !IO) :- % predicate implementation
( if Age >= 18 then
io.write_string("Adult\n", !IO)
else if Age >= 13 then
io.write_string("Teenager\n", !IO)
else
io.write_string("Child\n", !IO)
).