# Quick reference
## Overview
Mei is a strongly typed, polymorphic purely functional programming language inspired by Hope, Miranda and Haskell.
### Logo
### Mascot
### Downloads
Please note that at this moment the Mei interpreter is still in α-stage, so there may be bugs, crashes, undefined behavior, holocaust, data destruction, the heat death of the Universe etc.
#### Linux
- [x86_64](https://files.txlyre.website/d/software/mei/latest_x86_64/mei) (updated more frequently than others)
- [aarch64](https://files.txlyre.website/d/software/mei/latest_aarch64/mei)
- [armv7l](https://files.txlyre.website/d/software/mei/latest_armv7l/mei) (not updated for a long time!)
- [e2k](https://files.txlyre.website/d/software/mei/latest_e2k/mei) (not updated for a long time!)
#### Windows
- [x86_64](https://files.txlyre.website/d/software/mei/Windows_x86_64_0.9a_Jul_11_2023/Windows_x86_64_0.9a_Jul_11_2023.zip) (WIP; may have a lot of unexpected bugs; rarely updated)
#### Package manager
Also check the [Mei Package Manager](https://files.txlyre.website/d/software/mpm/) (requires Python 3.5+ and Git installed to work).
#### Automatic installer (Linux-only!)
Alternatively, you could use [this Bash script](https://files.txlyre.website/d/software/mei_installer/mei_installer.sh) that tries to install the interpreter and package manager automatically (requires to be run as **root**).
#### Motools (a kind of FFI for Mei)
You may be interested to see [motools](https://files.txlyre.website/d/software/motools/) – it's a C header that is used to create dynamic-link libraries loadable by the Mei interpreter.
#### Usage
```
usage: mei [-?hir] [-l file] [-c expr] file
-?h print this message and exit
-i read and execute a program from the standard input (stdin)
-r enter the interactive mode (a.k.a. Read-Eval-Print-Loop)
-s disable file system I/O functions (safe-mode)
-l file load the specified file first
-c expr execute the specified argument as an expression
file execute the specified file
-c help print the quick reference (or `-c help!' to display using the pager)
```
## Syntax
+ Comments (single line / multiple lines):
-- bla
-[ bla
bla
bla ]-
+ Number literals:
42; 42.5; 3.1415 -- Number literal (arbitrary precision). Type: num.
4_000; 40_000; 4_000_000 -- Underscores in numeric literals.
42I -- Integer. Type: int.
0xff; 0o777; 0b1011 -- Non-decimal integer literals.
3J -- Imaginary number. Type: imag.
3J+:3 -- Complex number. Type: com.
+ Other common scalar types:
'何'; 'a'; 'ж' -- Character (Unicode codepoint). Type: char.
B'q'; B'\x00'; B33 -- Byte (0-255 value). Type: byte.
True; False -- Boolean. Type: bool.
+ Collections:
[]; [1]; [1,2,3] -- List. Type: [a]; [num]; [num].
(); (1,); (1, 'a', "hi") -- Tuple. Type: (); (num,); (num, char, string).
-- String. Type: string or [char].
"Hello, World!" -- Syntactic sugar for a list of characters.
"Hello\nWorld!"
"Hello\xaWorld!"
R"(\w+)" -- Raw-string (no escape sequences).
M"Hello
world!" -- Multiline string.
W"Hello\n
world!" -- Multiline raw-string.
['a', 'b', 'c']
-- Bytestring. Type: bytes or [byte].
B"Hello,\x00World!" -- Syntactic sugar for a list of bytes.
-- ^ notice that bytestrings may contain NUL-bytes.
[B'a', B'c', B'd']
+ Structures:
Just 4; Error "too long" -- Algrebraic type instance. Type: maybe num; result a.
{x=4, y=5} -- Record. Type: {x :: num, y :: num}.
+ Function:
-- Application.
succ x
prime? 6
filter prime? [1,2,3,5,6,7,8]
-- Section (partial application).
(+ 3)
(2 *)
map (** 3)
-- Lambda-function (a.k.a. anonymous function).
\x -> x*(n-2)+1 -- Type: num -> num.
\(l, _) -> l -- Type: (a, b) -> a
+ Various expressions:
-- Integer range (inclusive). Type: [int].
[..5]
[4..9]
[8..(-1)]
[1..]
-- List comprehension (generators).
[x | x <- [..10], odd? x]
[(x, y) | x <- [..5], y <- "abcde"]
[(x, y) | x <- "abc" | y <- "def"] -- Equivalent to `zip "abc" "def"`.
[x | let k = 5, x <- map (*2) [..k]] -- Generator-local declaration.
-- 'do'-expression.
do print "Hello!";
5+5;
'a'
-- 'let'-expression (expression-local declarations).
map (\x -> let x' = random 0 x in x') [..10]
-- 'if'-expression.
if x <= 0 then
0
else
x
-- 'caseof'-expression (syntactic sugar for `\((h:t) -> h | [] -> error "empty") x)`.
case x of (h:t) -> h
| [] -> error "empty"
-- Using function as an infix operator.
(* 2) `map` [..5]
+ Function declaration:
-- Type signature declaration.
f :: num -> num -> num
f x y = x * y -- Function clause declaration.
f :: num => num -> num -- Syntactic sugar for `(num, num) -> num`.
-- Multiple clauses declaration.
fac 0 = 1
fac n = n * fac (n-1)
-- Guarded clause declaration.
fac n | n == 0 = 1
| otherwise = n * fac (n-1)
-- Polymorphic function.
left :: a -> b -> a
left l _ = l
-- Clause-local declarations using 'where'.
f n xs = r ++ l
where
(l, r) = splitAt n xs
+ Patterns:
-- List deconstruction.
f (x:xs) = (x, xs)
-- Algrebraic data type deconstruction.
f (Com x _) = x
-- Tuple deconstruction.
f ((x, _), _) = x
-- Alias.
f (l@x:xs) = x:xs:l
+ Operator declaration:
-- Operator declaration.
infixr 3 *!
(x *! y) = [x | _ <- [..y]]
+ Algrebraic data type (ADT) declaration:
data tree a = Leaf a
| Tree (tree a) (tree a)
-- Record.
type point = {x :: num, y :: num}
-- Type synonym.
type pair a b = (a, b)
+ Including other source files:
import utils
import "../utils"
import "txlyre/mbase64" -- Load package installed via MPM.
import "lib.mo" -- Loading MO library (see `Motools`).
### Examples
**Note**: these example are intended to be executed by the latest version of interpreter.
#### Hello World
```
main = print "Hello, World!"
```
#### Sierpinski triangle
```
sierpinski :: int -> strings
sierpinski n = foldl aggregate ["*"] [0..n-1]
where
aggregate t i = [sp ++ x ++ sp | x <- t] ++ [x ++ " " ++ x | x <- t]
where
sp = replicate (2 ** i) ' '
main = doEach print $ sierpinski 4
```
#### Mandelbrot
```
mandelbrot :: strings
mandelbrot = [[mandelbrot' x y 0 0 0 | x <- range (-2.1) 1.1 0.04] | y <- range (-1.3) 1.3 0.1]
where
mandelbrot' x y zr zi i = if i >= 100 || zi*zi+zr*zr >= 4 then
" .:-=+*#%@" ! (i%10)
else
mandelbrot' x y (zr*zr-zi*zi+x) (2*zr*zi+y) (i+1)
main = doEach putStrLn mandelbrot
```
#### Conway's Game of Life
```
type life_board = [(int, int)]
lifeStep :: life_board -> life_board
lifeStep board = [head g | g <- group $ sort $ concatMap neighbors board, viable? g]
where
neighbors (x, y) = [(x+dx, y+dy) | dx <- [-1..1], dy <- [-1..1], (dx, dy) != (0, 0)]
viable? [_, _, _] = True
viable? [cell, _] = board ? cell
viable? _ = False
printBoard :: life_board -> none
printBoard board = doEach print [[showCell (x, y) | x <- [l..g]] | y <- [l..g]]
where
showCell cell = if board ? cell then '#' else '.'
(l, g) = (minimum x y, maximum x' y')
where
(x, y) = head board
(x', y') = last board
main = iterate lifeStep glider
|> take 10
|> doEach print'
where
print' board = do
printBoard board;
putChar '\n'
glider = [(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)]
```
#### Brainfuck interprerer
```
data bf_state = BFState int [int]
bfNew :: bf_state
bfNew = BFState 0 (replicate 30_000 0)
bfChangeCell :: bf_state -> (int -> int) -> bf_state
bfChangeCell (BFState pointer memory) f =
BFState pointer (change memory pointer $ f $ memory ! pointer)
bfGetCell :: bf_state -> int
bfGetCell (BFState pointer memory) =
memory ! pointer
bfGoRight :: bf_state -> bf_state
bfGoRight (BFState pointer memory) | pointer >= 30_000 = error "bfGoRight: can't go further to the right"
| otherwise = BFState (pointer + 1) memory
bfGoLeft :: bf_state -> bf_state
bfGoLeft (BFState pointer memory) | pointer == 0 = error "bfGoLeft: can't go further to the left"
| otherwise = BFState (pointer - 1) memory
bfExtractLoop :: string -> int -> string
bfExtractLoop _ 0 = ""
bfExtractLoop "" _ =
error "bfExtractLoop: missing ]"
bfExtractLoop (c : cs) level =
c : bfExtractLoop cs level'
where
level' = case c of
| '[' -> level + 1
| ']' -> level - 1
| _ -> level
bfRunLoop :: string -> bf_state -> bf_state
bfRunLoop code state = if bfGetCell state == 0 then
state
else
bfRunLoop code $ bfRun state code
bfRun :: bf_state -> string -> bf_state
bfRun state "" = state
bfRun state (c : cs) = case c of
| '+' -> bfRun (bfChangeCell state succ) cs
| '-' -> bfRun (bfChangeCell state pred) cs
| '>' -> bfRun (bfGoRight state) cs
| '<' -> bfRun (bfGoLeft state) cs
| '.' -> do putChar $ fromCharCode $ bfGetCell state; bfRun state cs
| ',' -> bfRun (bfChangeCell state (\_ -> charCode? getChar)) cs
| '[' -> bfRun (bfRunLoop loop' state) $ drop (succ $ length loop') cs
| _ -> bfRun state cs
where
loop' = init $ bfExtractLoop cs 1
main = bfRun bfNew getLines
```
#### HTTP GET request
```
main = getRequest "https://api.ipify.org"
|> explode B"\r\n"
|> last
|> bytesToString
|> print
```
#### Dead simple HTTP server
```
response = "Hello World!"
main = forever listen $ makeSocketServer False 8080
where
listen s = respond $ acceptSocketConnection s
respond s = do print $ "< " ++ (first $ socketGetAddress s);
writeSocket s B"HTTP/1.1 200 OK\r\n";
writeSocket s B"Content-Type: text/html\r\n";
writeSocket s $ B"Content-length: " ++ (stringToBytes $ show $ length? response') ++ B"\r\n";
writeSocket s B"\r\n";
writeSocket s response';
closeSocket s
response' = stringToBytes response
```
#### Regular expressions
**Note**: Mei uses a custom regular expression engine. Check the end of this document for details.
```
main = do if s ~= "[[:o:]][[:w:]]*" then
print "This is a valid identifier."
else
print "This is not a valid identifier.";
print $ "Numbers: " ++ (intercalate ", " $ s ~~ R"(\d+)");
print $ "Splitted by punctuation: " ++ (intercalate " " $ s ~/ "[[:p:]]");
print $ "Make all numbers negative: " ++ (rewrite R"(? getRequest
|> bytesToString
|> explode "\r\n\r\n"
|> last
|> jsonRead
|> validate
where
encodeQS qs = "?" ++ (intercalate "&" $ encodeQS' qs [])
encodeQS' [] b = b
encodeQS' ((k, v):qss) b = encodeQS' qss $ b <: (k # "=" # v)
validate (JObject [("ok", JBool True), resp]) = resp
validate (JObject [("ok", JBool False), ("error_code", JNum code), ("description", JString desc)]) = error $ "telegramAPIRequest: API error (code " # code # "): " # desc
validate _ = error "telegramAPIRequest: unknown error"
main = while (const True)
(
id -> processUpdates $ telegramAPIRequest token "getUpdates" [("offset", id)]
)
0
where
processUpdates ("result", JArray updates) = do doEach processUpdate updates;
if empty? updates then
0
else
updates
|> last |> (\JObject a -> a)
|> lookup "update_id" |> (\Just (JNum x) -> x)
|> succ
processUpdate (JObject update) = case update of
[("update_id", _), ("message", JObject message)] -> processMessage message
| _ -> None
processMessage message = case getMessageText message of
"/ping" -> replyTo message "pong"
| _ -> None
replyTo message text = telegramAPIRequest token "sendMessage" [
("chat_id", showNum $ getMessageChatId message),
("reply_to_message_id", showNum $ getMessageId message),
("text", text)
] |> return
getMessageChatId message = lookup "chat" message
|> (\Just (JObject a) -> a)
|> lookup "id"
|> (\Just (JNum chat_id) -> chat_id)
getMessageId message = lookup "message_id" message
|> (\Just (JNum message_id) -> message_id)
getMessageText message = lookup "text" message
|> (\Just (JString s) -> s)
token = getEnv "BOT_TOKEN"
```
### Builtins (this section is WIP)
#### (+) :: num -> num -> num (left 4)
#### (-) :: num -> num -> num (left 4)
#### (\*) :: num -> num -> num (left 3)
#### (/) :: num -> num -> num (left 3)
#### (//) :: num -> num -> num (left 3)
#### (\*\*) :: num -> int -> num (right 2)
#### (%) :: num -> num -> num (left 3)
#### (==) :: a -> a -> bool (right 5)
#### (!=) :: a -> a -> bool (left 5)
#### (<=) :: a -> a -> bool (left 5)
#### (>=) :: a -> a -> bool (left 5)
#### (<) :: a -> a -> bool (left 5)
#### (>) :: a -> a -> bool (left 5)
#### not :: bool -> bool
#### (&&) :: bool -> bool -> bool (right 6)
#### (||) :: bool -> bool -> bool (right 7)
#### (~) :: int -> int
#### band :: int -> int -> int (right 7)
#### bor :: int -> int -> int (right 7)
#### xor :: int -> int -> int (right 7)
#### << :: int -> int -> int (right 7)
#### >> :: int -> int -> int (right 7)
#### ($) :: (a -> b) -> a -> b (right 10)
#### (|>) :: a -> (a -> b) -> b (left 10)
#### (.) :: (b -> c) -> (a -> b) -> a -> c (right 9)
#### (++) :: [a] -> [a] -> [a] (right 4)
#### (:) :: a -> [a] -> [a] (right 1)
#### (<:) :: [a] -> a -> [a] (left 1)
#### (!) :: [a] -> int -> a (left 2) (raises error)
+ Returns Nth element of the list.
#### (?) :: [a] -> a -> bool (left 2)
+ Returns **True** if the list containts an element, **False** otherwise.
#### (~=) :: string -> string -> bool (right 5) (raises error)
+ Returns **True** if the string matches a regular expression, **False** otherwise.
#### (~~) :: string -> string -> strings (right 5) (raises error)
+ Extracts every match of a regular expression in the string.
#### charCode? :: char -> int
#### fromCharCode :: int -> char (raises error)
#### error :: string -> a
#### try :: a -> result a
#### ok? :: result a -> bool
#### error? :: result a -> bool
#### fromResult :: a -> result a -> a
#### length? :: [a] -> int
#### head :: [a] -> a (raises error)
#### tail :: [a] -> [a] (raises error)
#### init :: [a] -> [a] (raises error)
#### last :: [a] -> a (raises error)
#### take :: int -> [a] -> [a]
#### drop :: int -> [a] -> [a]
#### until :: (a -> bool) -> (a -> a) -> a -> a
#### reduce :: (a -> a -> a) -> [a] -> a (raises error)
#### scan :: (a -> a -> a) -> [a] -> [a]
#### map :: (a -> b) -> [a] -> [b]
#### filter :: (a -> bool) -> [a] -> [a]
#### print :: a -> none
#### put :: a -> none
#### show :: a -> string
+ Returns a string representation of the given value.
#### (#) :: a -> b -> string (right 5)
#### readFile :: string -> string (raises error)
#### writeFile :: string -> string -> none (raises error)
#### readBinaryFile :: string -> bytes (raises error)
#### writeBinaryFile :: string -> bytes -> none (raises error)
#### appendFile :: string -> string -> none (raises error)
#### appendBinaryFile :: string -> bytes -> none (raises error)
#### fileExists? :: string -> bool
#### isFile? :: string -> bool
#### isDirectory? :: string -> bool
#### fileSize? :: string -> int (raises error)
#### listDirectory :: string -> strings (raises error)
#### removeDirectory :: string -> bool
#### createDirectory :: string -> int -> bool
#### getCurrentTime :: num
#### formatTime :: string -> int -> string (raises error)
#### executeSystem :: string -> (bytes, int) (raises error)
#### exec :: string -> string (raises error)
#### getCurrentDirectory :: string
#### setCurrentDirectory :: string -> bool
#### getEnv :: string -> string (raises error)
#### setEnv :: string -> string -> bool
#### getProcessID :: int
#### getParentProcessID :: int
#### killProcess :: int -> int -> bool
#### forkProcess :: int (raises error)
#### waitProcess :: int -> int -> (int, int)
#### gather :: [a] -> none (raises error)
#### executeFile :: string -> bool -> strings -> strings -> none (raises error)
#### makePipe :: int (raises error)
#### readPipe :: int -> int -> bytes (raises error)
#### writePipe :: int -> bytes -> none (raises error)
#### closePipe :: int -> none (raises error)
#### makeSocketServer :: bool -> int -> int (raises error)
#### makeSocketClient :: bool -> string -> int -> int (raises error)
#### acceptSocketConnection :: int -> int (raises error)
#### readSocket :: int -> int -> bytes (raises error)
#### writeSocket :: int -> bytes -> none (raises error)
#### closeSocket :: int -> none (raises error)
#### socketIsServer :: int -> bool (raises error)
#### socketIsClosed :: int -> bool (raises error)
#### resolveAddress :: string -> strings (raises error)
#### httpRequest :: string -> int -> bool -> string -> bytes (raises error)
#### getRequest :: string -> bytes (raises error)
#### postRequest :: string -> string -> string -> bytes (raises error)
#### zCompress :: bytes -> bytes (raises error)
#### zDecompress :: bytes -> bytes (raises error)
#### getLine :: string
#### getLines :: string
#### getChar :: char (raises error)
#### getBytes :: bytes
#### getByte :: byte (raises error)
#### open :: string -> int -> int -> int (raises error)
#### read :: int -> int -> bytes (raises error)
#### write :: int -> bytes -> int (raises error)
#### close :: int -> bool
#### sync :: int -> bool
#### seek :: int -> int -> int -> int
#### quit :: int -> a
#### putChar :: char -> none
#### putByte :: byte -> none
#### putBytes :: bytes -> none
## Regular expressions syntax
### Metacharacters
```
. matches any single character.
\ escapes the next character.
^ matches the beginning of line.
$ matches the end of line.
| alternation.
```
### Brackets
```
() grouping.
[] character class.
[a-z] character range.
(?mis) sets flags: m - multiline, i - case insensitive, s - stretchy.
[:...:] special character class.
(?^...) negative grouping.
(?=...) positive lookahead.
(?!...) negative lookahead.
(?<=...) positive lookbehind.
(?...) atomic grouping.
(?0..9) calls subgroup.
(?<...>) calls named subgroup.
(?N:...) refers named subgroup.
(?N<...>) named grouping.
(?:...) subpattern.
(?|...) resetting grouping.
```
### Special character classes:
```
[:x:] [0-9A-Fa-f]
[:d:] [0-9]
[:s:] [ \t\n\r\f\v]
[:N:] [^\n]
[:a:] [A-Za-z]
[:o:] [A-Za-z_]
[:w:] [A-Za-z0-9_]
[:l:] [a-z]
[:u:] [A-Z]
[:p:] [-!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~]
```
### Escapes
```
\x... matches character with the specified hexadecimal code.
\o... matches character with the specified octal code.
\d matches any digit.
\D matches any non-digit.
\s matches any space character.
\S matches any non-space character.
\w matches any alphanumeric character.
\p{...} matches any character within the specified Unicode property.
\N{...} matches a character by its Unicode name.
\0..9 backref.
\K sets match start.
\A matches the beginning of the subject.
\Z matches the end of the subject.
\b matches at word boundary.
\B matches at not word boundary.
\Q starts literal.
\q terminates literal.
```
### Quantifiers
```
* match 0 or more times.
+ match 1 or more times.
? match 0 or 1 times.
{n} match n times.
{n,m} match n to m times.
{n,} match at least n times.
{,n} match at most n times.
```