Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operators

Operators in milang are ordinary functions with special syntax. Every operator can be used in prefix form by wrapping it in parentheses, and any function can be used infix with backtick syntax.

Arithmetic

OperatorMeaning
+Addition (also string concatenation)
-Subtraction
*Multiplication
/Division (integer for ints, float for floats)
%Modulo (integers only)
**Exponentiation (integer exponent)
a = 2 + 3
b = 10 - 4
c = 3 * 7
d = 10 / 3
e = 10 % 3
f = 2 ** 10
build =  {target = c, os = linux, arch = x86_64}
a = 5
b = 6
c = 21
d = 3
e = 1
f = 1024

Float division produces a decimal result:

a = 7.0 / 2.0
b = 3.14 * 2.0
build =  {target = c, os = linux, arch = x86_64}
a = 3.5
b = 6.28

Comparison

Comparison operators return 1 (true) or 0 (false). == and /= work structurally on records, lists, and strings.

OperatorMeaning
==Equal
/=Not equal
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal
a = 3 == 3
b = 3 /= 4
c = 5 > 2
d = [1, 2] == [1, 2]
e = "hello" == "hello"
build =  {target = c, os = linux, arch = x86_64}
a = 1
b = 1
c = 1
d = 1
e = 1

Logical

Logical operators short-circuit and return 1 or 0. not is a function, not an operator.

a = 1 && 1
b = 1 && 0
c = 0 || 1
d = 0 || 0
e = not 0
f = not 1
build =  {target = c, os = linux, arch = x86_64}
a = 1
b = 0
c = 1
d = 0
e = 1
f = 0

Short-circuit evaluation means the right-hand side is never forced when the left side determines the result:

safe = 0 && (1 / 0)   -- 0, right side never evaluated

String Concatenation

The + operator also concatenates strings:

greeting = "hello" + " " + "world"
build =  {target = c, os = linux, arch = x86_64}
greeting = hello world

Cons

The : operator prepends an element to a list. It is right-associative.

xs = 1 : 2 : 3 : []
build =  {target = c, os = linux, arch = x86_64}
xs = [1, 2, 3]

Pipe

x |> f is syntactic sugar for f x, enabling left-to-right data flow:

double x = x * 2
result = 5 |> double
build =  {target = c, os = linux, arch = x86_64}
double = <closure>
result = 10

Composition

f >> g composes left-to-right (\x -> g (f x)). f << g composes right-to-left (\x -> f (g x)).

double x = x * 2
inc x = x + 1
pipeline = double >> inc
a = pipeline 5
build =  {target = c, os = linux, arch = x86_64}
double = <closure>
inc = <closure>
pipeline = <closure>
a = 11

Record Merge

a <- b produces a new record with all fields from a, overwritten by fields from b:

base = {x = 1; y = 2; z = 3}
updated = base <- {x = 10; z = 30}
build =  {target = c, os = linux, arch = x86_64}
base =  {x = 1, y = 2, z = 3}
updated =  {x = 10, y = 2, z = 30}

Block Argument

f => body passes body as the last argument to f, where body is an indented block. This is useful for passing multi-line expressions cleanly:

values => 
  1
  2
  3

The => operator is syntactic sugar — f => body is equivalent to f body. The block is parsed as an indented scope, making it natural for DSLs and builder patterns:

ann ffi ns = values =>
  ffi.struct "Point" |> ffi.field "x" "int32" |> ffi.field "y" "int32"
  ffi.out "decompose" |> ffi.param 1 "int32"

Operators as Functions

Wrap any operator in parentheses to use it in prefix (function) position:

a = (+) 3 4
b = (*) 5 6
total = fold (+) 0 [1, 2, 3, 4, 5]
build =  {target = c, os = linux, arch = x86_64}
a = 7
b = 30
total = 15

Functions as Infix Operators

Surround a function name with backticks to use it as an infix operator:

bigger = 3 `max` 7
smaller = 3 `min` 7
build =  {target = c, os = linux, arch = x86_64}
bigger = 7
smaller = 3

User-Defined Operators

You can define custom operators just like functions. Precedence and associativity are set with the parse domain :!. See the Parse Declarations and User Operators chapters for details.

(<=>) a b = if (a == b) 0 (if (a > b) 1 (0 - 1))
(<=>) :! {prec = 30; assoc = Left}