Learn Intlang by Example
If you (like me) learn best by seeing examples and are familiar with functional programming language this should get you up to speed.
let (rec) (and)
let a = 5
binds 5 to a
let rec fib = \n. if 1 < n then (fib (n-1) + fib (n-2)) else (n==1) end
defines the recursive function fib.
let rec collatz = \n cnt. if n == 1 then cnt else if n % 2 == 0 then collatz_even n cnt else collatz_odd n cnt end end
and collatz_even = \n cnt. collatz (n / 2) (cnt + 1)
and collatz_odd = \n cnt. collatz (3*n + 1) (cnt + 1)
defines three functions that are all mutually recursive
main
let main = \() ()
a top-level (global) let binding with the name main must be of type unit->unit and is called after all top-level binding are evaluated.
Expressions
The sub sections that follow introduce different types of expressions. These expressions can be used as right hand sides of top-level let bindings.
lambdas
\x. x
is the identity function
\f g x. f (g x)
is 3 lambdas written as one, it is equivalent to:
\f. \g. \x. f (g x)
Note: In intlang all functions/lambdas are curried by default.
\i : i32. i
is the identity function but annotates that i must be of i32 type
\i : i32 => i32. i
also annotates that an i32 must be returned
\(i : i32) (j : i32) => i32. i+j
for more than argument it looks like this
let (rec) in
let a = 5 in a+3
binds 5 to a and then evaluates a+3 with this binding
let rec fib =
\n. if 1 < n then (fib (n-1) + fib (n-2)) else (n==1) end
in fib 4
binds the recursive fibonacci function to fib and evaluates fib 4 with the new binding
sequence
a;
b
Evaluates a and then b. A must be of unit type. The parsing on this can be unintuitive at them moment: ; has lowest precedence (even lower than let in) so it is often necessary to put parenthesis around code with ;. Might change this in the future.
unit
()
is the unit literal
\() 5
is a special lambda expecting unit as argument and returns 5 in this example.
i32
4
is a i32 literal
i8 / strings
'f'
is an i8 literal
'\xff'
is an i8 literal in hex notation
if than else end
if a then 4 else 7 end
returns 4 if a (needs to be i32) is non zero and 7 otherwise
unary and binary operation
For both i32 and i8 there are a number of unary and binary operations:
Bopi32:
== != < > <= >=
<u >u <=u >=u
+ - * / %
/u %u
& | ^
<< >> >>u
Uopi32:
- ~
Bopi8:
==i8 !=i8 <i8 >i8 <=i8 >=i8
these return i32 (comparisons)
+i8 -i8
&i8 |i8 ^i8
these return i8
Uopi8:
-i8 ~i8
tuples
let t = (4,4) in ...
creates a new tuple bound to t with two i32 values both 4.
let (a,b) = t in ...
Extracts the fields form tuple t.
let (a,_) = t in ...
_ can be used to ignore some part of the tuple
vectors
vec[1,2,3]
creates a vector with i32 elements 1,2 and 3
vecmk[99,5]
creates a vector of length 5 with all elements set to 99
vecmk[77,4,2]
creates a vector of length 4 with all elements set to: vectors of length 2 with all elements set to 77. This is a 2-D vector, vecmk can create N-D vectors by just adding more dimension sizes.
let v = vec[1,2,3] in
vecget[v, 0]
gets the first (zero indexed) element from v ie. 1
let v = vec[vec[1,2,3], vec[11,22,33]] in
vecget[v, 1, 2]
gets the third element of the second element of v ie. 33
let v = vec[1,2,3] in
vecset[v,55, 1]
creates a new vector what is equal in value to v but has the second element set to 55 ie. vec[1,55,3]
let v = vec[vec[1,2,3], vec[11,22,33]] in
vecset[v, 44, 0, 2]
creates a new vector what is equal in value to v but has the third element of the first element set to 44 ie. the same as vec[vec[1,2,44], vec[11,22,33]]
vecget and vecset can index into N-D vectors like this by just adding more indexes
let v = vec[1,2,3,4] in
vecslice[v,1,2]
creates a new vector that is equal in value to a slice of v starting at offset 1 and of length 2 ie. the same as vec[2,3]
let v = vec[1,2] in
vecextend[v,3,2]
creates a new vector that is equal in value to v with 3 appended twice ie. the same as vec[1,2,3,3]
let v = vec[1,2] in
vecextend[v,5,-3]
creates a new vector that is equal in value to v with 5 prepended three times ie. the same as vec[5,5,5,1,2]
strings
"intlang"
is a string literal. strings are just sugar for vector literals with i8 elements. "intlang" is the same as vec['i','n','t','l','a','n','g']
comments
-- this is a comment
-- starts a line comment.
builtins
The following functions are builtin to the language:
readi8has typeunit->i8and reads a char (from stdin)writei8has typei8->unitand writes a char (from stdout)flushhas typeunit->unitand flushes stdouti32_to_i8has typei32->i8and truncates a i32 to an i8i8_to_i32has typei8->i32and zero extends an i8 to i32
include
include io
let main = \() (
io.write_ln "hello, intlang"
)
Includes the io module (.intlang file) and used the write_ln form it to print "hello, intlang".
Intlang also supports relative included (include location relative to current module) but it is disabled here.
The Lib contains all things that can be included in the Playground.
IO
The io library is probably the most useful when interacting with intlang programs. Here are probably the most useful functions:
io.read_ln: takes an[i8](string) and prints it to the terminalio.write_ln: takesunit(nothing), reads a line from the terminalio.readln_i32: takes ani32(int) and prints it to the terminal (with trailing line break)io.writeln_i32: takesunit(nothing), reads a line containing ani32from the terminalio.readln_i32vec: takes ani32vector and prints it to the terminal (comma separated and with trailing line break)io.writeln_i32vec: takesunit, reads a line containing comma separatedi32's from the terminal
More formal vector operations semantics
-
All kinds of indexes, dimensions and offsets need to be of i32 type
-
Vectors are 0-indexed ie. the first element of a vector has index 0
-
All elements of a vector must be of the same type
-
Vectors can have element types: vector, i32 and i8 (might change in the future)
-
<..>denotes not just an expression but rather a comma separated list of expressions -
vec[<litlist>]: create a vector<litlist>must be a comma separated list of literals- Returns a vector of length equal to the number of elements in
<litlist>, with the order of<litlist>preserved and the left most element of<litlist>at index 0
-
veclen[v]: get the length of a vector- returns the length of the vector
vas ani32
- returns the length of the vector
-
vecget[v, <idxlist>]: access a vector<idxlist>must be a comma separated list of indexes- If
<idxlist>is emtpyvis returned - Else
<idxlist>has a left most elementidxthenvecget[v_inner, <idxtail>]is returned, wherev_inneris the value stored at indexidxinvand<idxtail>is<idxlist>without its left most element
-
vecset[v, value, <idxlist>]: create e new vector based on the old one<idxlist>must be a comma separated list of indexes- If
<idxlist>is emtpyvalueis returned - Else
<idxlist>has a left most elementidxthenv'is returned wherev'is equal (in value not physical) tovbut has the value at indexidxset tovecset[v_inner, value, <idxtail>], wherev_inneris the value stored at indexidxinvand<idxtail>is<idxlist>without its left most element.
-
vecmk[lit,<dimlist>]: initialize a vector<dimlist>must be a non empty comma separated list of indexes- If
<dimlist>has a single elementdimthe result is a vector of lengthdimwith valuelitat at every index. - Else
<dimlist>has more than one element with a left most elementdimheadand the rest of the list<dimtail>then the result is a vector of lengthdimheadwithvecmk[lit, <dimtail>]at every index
-
vecslice[v, start, len]: slice a vector- returns vector equal (in value not physical) to a slice/section of
vstarting at indexstartof lengthlen.
- returns vector equal (in value not physical) to a slice/section of
-
vecextend[v, lit, off]: extend / expand a vector- If
offis positive: returns a vector equal (in value not physical) tovwithvecmk[lit, off]appended - If
offis negative: returns a vector equal (in value not physical) tovwithvecmk[lit, -off]prepended
- If