Comments

Identifiers

Kinds

Simple Kinds

Compound Kinds

Kind Annotations & Conversion

Custom Kinds & Enums

Data Types

Number

Floating-Point Literals

Integer Literals

Rational Literals

Complex Literals

String

String Concatenation

Conversion to String

Boolean

Logical Operations

Comparison Operations

Filtering

Atom

Empty

Variables

Data Structures

Matrix

Broadcast Operations

Tuple

Element Access

Destructuring

Record

Table

Set

Map

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 x and y of types T and U, and 3 rows)

  • Tuple: <(T,U)> (An ordered pair)

  • Map: <{K:V}> (Maps K to V)

  • Set: <{T}> (A set of unique T values)

Kind Annotations & Conversion

Annotations constrain variables or cast literals.

x:=42<u8>--

Literal annotation

y<u8>:=10--

Variable constraint

m:=[
1
2
3
4
5
6
]
--

[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>:=[
10
20
30
]

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 integer

      • i16 - 16-bit signed integer

      • i32 - 32-bit signed integer

      • i64 - 64-bit signed integer

      • i128 - 128-bit signed integer

    • Unsigned - Can only represent zero and positive values

      • u8 - 8-bit unsigned integer

      • u16 - 16-bit unsigned integer

      • u32 - 32-bit unsigned integer

      • u64 - 64-bit unsigned integer

      • u128 - 128-bit unsigned integer

  • Float - Decimal numbers with fractional components

    • f32 - 32-bit floating-point number

    • f64 - 64-bit floating-point number

  • Rational - Numbers represented as fractions of integers

    • r64 - 64-bit rational number

  • Complex - 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, where a is the numerator and b is the denominator

  • Complex numbers, expressed in the form a + bi or a + bj, where a is the real part and b is 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 f64

3.14-0.0012.5e+3.-1.2e-2.

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:

x:=1/2--

Represents 0.5

y:=2/4--

Also represents 0.5

xy--

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:

first:="Hello"second:="World"greeting:=first+", "+second+"!"

String concatentation is broadcast element-wise over matrices of strings:

prefix:="Item"letters:=[
"A"
"B"
"C"
]
labels:=prefix+letters

If both operands are matrices of strings, they must have compatible dimensions:

m1:=[
"a" "c"
"b" "d"
]
m2:=[
"1" "3"
"2" "4"
]
result:=m1+m2

Conversion to String

All values can be converted to strings explicitly.

x:=42s<string>:=x

Use <[string]> to create a matrix of strings:

m<[string]:2,2>:=[
1
2
3
4
]

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:

55342<10

Filtering

An array of logical values can be used as an index to filter another array:

x:=[
1
2
3
4
]
ix:=x>2x[ix]--

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:=:fooab--

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:=123

By default, all variables are immutable. To create a mutable variable, prefix the variable name with ~:

~y:=456 y = y+1 --

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>:=255

Data 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

[
1
2
3
]
--

Column Vector

[
1 2 3
]
--

Matrix

[
1 3
2 4
]
--

Vector from expressions

[
1+2 3+4
]

Broadcast Operations

If matrices have the same dimension, standard arithmetic operations are performed element-wise:

m1:=[
1 4
2 5
3 6
]
m2:=[
7 10
8 11
9 12
]
m1+m2--

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:

m:=[
1
2
3
4
5
]
m+10--

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.

q:=(10,"b",true)q.1

Destructuring

Tuples can be destructured into variables:

a:=(10,11,12)(x, y, z):=ax+y+z

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