Learn Mech in Fifteen Minutes
Comments
-- Single line comment. // Also a single line comment.
Mech does not support multiline comments. Instead, Mech encourages literate programming through a Markdown-esque syntax called Mechdown, exemplified by this document.
Comments are therefore to be used primarily inside of code blocks.
Comments support paragraph formatting, such as bold, underline, links, etc. For example:
-- Comments support **bold**, __underline__, [links](/foo/bar), etc.--
Comments support bold, underline, links, etc.
Identifiers
Identifiers start with letters or most UTF-8 encoded characters, and can contain alphanumeric, most emojis, /, *, +, -, and ^ characters.
Hello-Word
io/stdout
Δx^2
🤖
A*
The preferred identifier case in Mech is kebab-case (words deliniated by dashes). Slashes also allow identifiers to be scoped to a particular namespace.
To maintain a consistent style, Mech does not support underscores in identifiers and they will result in a syntax error.
Kinds
A kind describes the type of data a value can hold. Every value in Mech has a kind, and kinds themselves are values.
Kinds describe the data type, not mutability, which is a property of the data binding.
Simple Kinds
Kinds are written inside angle brackets < >. The following kinds represent the built-in data types in a base Mech distribution:
Category | Examples |
|---|---|
Signed Integers | <i8>, <i16>, <i32>, <i64>, <i128> |
Unsigned Integers | <u8>, <u16>, <u32>, <u64>, <u128> |
Complex Numbers | <c64> |
Rational Numbers | <rs64> |
Floats | <f32>, <f64> |
Text | <string> |
Logic | <bool> |
Kind | <<kind>> |
Empty | <_> |
Compound Kinds
Kinds compose structurally to describe collections.
Matrix: <[T]:2,3> (A 2x3 matrix of
T)Table: <|x<T>,y<U>|:3> (A table with columns
xandyof typesTandU, and 3 rows)Tuple: <(T,U)> (An ordered pair)
Map: <{K:V}> (Maps
KtoV)Set: <{T}> (A set of unique
Tvalues)
Kind Annotations & Conversion
Annotations constrain variables or cast literals.
x:=42<u8>--Literal annotation
y<u8>:=10--Variable constraint
m:=[123456]--[1]:1,6
n<[i32]:2,3>:=m--Reshapes and converts to 2x3 i32 matrix
Custom Kinds & Enums
Aliases:
You can create aliases for semantic clarity.
<point3>:=<[f64]:3>p<point3>:=[102030]
Enums:
Enums define a fixed set of variants using atoms.
<color>:=red|green|bluemy-color<color>:=:color/green--a valid color
other-color<color>:=:color/yellow--error: invalid variant
Data Types
Number
Numbers in Mech can be classified into the following categories:
Integers - Whole numbers, which can be positive, negative, or zero
Signed - Can represent both negative and positive values
i8- 8-bit signed integeri16- 16-bit signed integeri32- 32-bit signed integeri64- 64-bit signed integeri128- 128-bit signed integerUnsigned - Can only represent zero and positive values
Float - Decimal numbers with fractional components
Rational - Numbers represented as fractions of integers
r64- 64-bit rational numberComplex - Numbers with real and imaginary components
c64- 64-bit complex number
Numeric literals can be expressed in various formats:
Floating-point numbers, expressed in standard decimal notation and scientific notation
Integers, expressed in decimal (base 10), hexadecimal (base 16), octal (base 8), and binary (base 2)
Rational numbers, expressed as fractions
a/b, whereais the numerator andbis the denominatorComplex numbers, expressed in the form
a + biora + bj, whereais the real part andbis the imaginary part
Floating-Point Literals
Floating-point literals are used to represent decimal numbers with fractional components. They are flexible in that they can represent both very large and very small numbers, and also can represent integer values between -1.7e308 and 1.7e308 for f64, and between -3.4e38 and 3.4e38 for f32.
Floating-point literals are expressed in standard decimal notation or scientific notation:
42--inferred as
3.14-0.0012.5e+3.-1.2e-2.f64
Each value is inferred as f64 unless otherwise specified. To specify a specific type, floating-point literals can also be suffixed with a specific kind:
42<f32>3.14<f32>-0.001<f64>2.5e+3.<f32>-1.2e-2.<f64>
Integer Literals
Integer literals can be written in the following formats:
0d42--decimal
0x2ABC--hexadecimal
0o654--octal
0b101010--binary
Decimals without a prefix are inferred as floating-point numbers (f64), while those with the 0d prefix are inferred as signed integers (i64). Each of the other formats are also inferred as i64 unless otherwise specified. Any integer literal can be suffixed with a specific kind to indicate its type:
42u8-- unsigned 8-bit integer
Decimal literals can be signed or unsigned. Signed literal types include i8, i16, i32, i64, and i128. Unsigned literal types include u8, u16, u32, u64, and u128.
Rational Literals
Rational numbers are used to represent fractions exactly as ratios of two integers. They are expressed in the form a/b, where a is the numerator and b is the denominator.
Rational numbers are written as follows:
3/4-5/27/7--A whole number represented as a rational
Two rational literals can represent the same numeric value but have different representations:
Represents 0.5
Also represents 0.5
Evaluates to true
Complex Literals
Complex numbers are used to represent numbers with both real and imaginary components. They are expressed in the form a + bi or a + bj, where a is the real part and b is the imaginary part.
Complex numbers are written as follows:
3+4i1+-2i0+1i
The imaginary unit can be represented by either i or j. Both representations are equivalent and can be used interchangeably.
String
String literals are written using double quotes ".
"""Hello, world!""Multi Line String""Unicode: 🤖🦾🦿"
Strings may contain spaces and punctuation. Whitespace inside a string literal is preserved.
Escape sequences may be used for special characters:
"line 1\nline 2" "quote: \"" "tab\tseparated" "backslash: \\"
When rendered:
"line 1 line 2""quote: """tab separated""backslash: \"
Raw string literals can be created using triple double quotes """. In this form, escape sequences are not processed, and all characters are taken literally, including backslashes and quotes.
"""C:\Users\Name\Documents""" """This is a "raw" \string\ literal."""
When rendered:
"C:\Users\Name\Documents""This is a "raw" \string\ literal."
String Concatenation
Strings can be concatenated using the + operator:
String concatentation is broadcast element-wise over matrices of strings:
If both operands are matrices of strings, they must have compatible dimensions:
Conversion to String
All values can be converted to strings explicitly.
Use <[string]> to create a matrix of strings:
This converts a <[f64]:1,4> (row matrix of floats) to a <[string]:2,2> (square matrix of strings).
Boolean
Boolean literals are written using the keywords true and false.
truefalse
Several alterantive forms are also accepted:
✓--true
✗--false
Logical Operations
Boolean values support the following logical operations:
¬true--false
true&&false--false
true||false--true
Comparison Operations
Comparison operators produce boolean results:
5⩵53≠42<10
Filtering
An array of logical values can be used as an index to filter another array:
selects elements where ix is true
Atom
Atoms are written using a leading colon : followed by an identifier:
:foo:help:my-atom:🐦:Δt
Atom names may be any valid identifier, including Unicode characters.
Two atoms with the same name are identical:
a:=:foob:=:fooa⩵b--evaluates to true
Empty
An empty value is written using one or more underscores _:
____
Empty is used to represent missing values in tables and skipped elements in tuple destructuring.
Variables
Variables are created using the := operator:
x:=123By default, all variables are immutable. To create a mutable variable, prefix the variable name with ~:
valid
Semicolons can be used to write multiple statements on one line:
a:=123b:=456
You can use a kind annotation in the variable declaration to constrain the variable to a specific kind:
z<u8>:=255Data Structures
Matrix
Declare a matrix using square brackets [].
Use semicolons ; or newlines to separate rows.
Use commas , or spaces to separate columns.
[1,2,3] -- A row vector (1x3) [1 2 3] -- The same row vector [1;2;3] -- A column vector (3x1) [1 2 3] -- The same column vector [1,2;3,4] -- A matrix (2x2) [1 2 3 4] -- The same matrix [1 + 2; 3 + 4] -- Elements can be expressions
By default, all matrices are rank 2, meaning they have rows and columns. Elementes are indexed in a [row, column] format.
When parsed and formatted, they look like this:
Row Vector
[Column Vector
[Matrix
[Vector from expressions
[Broadcast Operations
If matrices have the same dimension, standard arithmetic operations are performed element-wise:
Adds the two matrices element-wise
If one of the operands is a scalar, it will be added or subtracted from each element of the matrix:
Adds 10 to each element of the vector
Tuple
A tuple is a collection of values of different data types stored in a single entity, allowing for the storage and manipulation of multiple values together.
Tuples are defined using parentheses () with elements separated by commas.
(1, true)
("Hello", 42, false)
(1, ("Hello", false))
When parsed and rendered, this code will appear as:
(1,true)--two-tuple of f64 and bool
("Hello",42,false)--three-tuple of string, f64, and bool
(1,("Hello",false))--nested tuple
Element Access
Tuple elements are accessed by their 1-based index using the . operator.
Destructuring
Tuples can be destructured into variables:
Record
Records are written using curly braces {} with named fields. Each field is written as name: value and fields are separated by commas.
{ x: 1, y: true }
{ name: "Alice", age: 42, active: true }
When parsed and rendered, this code will appear as:
{x:1, y:true}{name:"Alice", age:42, active:true}
Records may be written inline or across multiple lines for readability:
{
x: 1.2,
y: true,
label: "pt1"
}
Table
Tables are defined using the pipe | character. Each column is defined with a name and a kind, which describes the type of data it holds.
| x<f32> y<bool> | | 1.2 true | | 1.3 false |
This creates a table with two columns, x of kind f32 and y of kind bool:
x<f32> y<bool> 1.2 true 1.3 false
You can write tables inline as well:
| x<f32> y<bool> | 1.2 true | 1.3 false |
Set
A set is an unordered collection of unique elements. They are defined using curly braces {}.
Elements are separated by commas ,. Duplicate elements are automatically removed.
{}--An empty set
{1, 2, 3}--A set of numbers
{"a", "b", "c"}--A set of strings
{1, 1, 2, 3}--Duplicates are removed
Map
You can define maps using curly braces {} with a colon : to separate keys from values.
{"a":1}--A single key-value pair
{"a":10, "b":20}--Multiple key-value pairs
{"a":{"b":2}}--Nested maps
{"a":10, "b":20, "c":30}--Multi-line format