KC3 tutorial and examples

Here are a few ikc3 examples to play with.

Maps

KC3 maps are key-value stores, you can use any tag as a key and associate a value to it.

You can use destructuring to access KC3 maps :

ikc3> a = %{id: 1, title: "My title", message: "Hello, world !"}
%{id: 1, title: "My title", message: "Hello, world !"}
ikc3> a = %{}
%{id: 1, title: "My title", message: "Hello, world !"}
ikc3> %{id: id, title: "My title", message: message} = a
%{id: 1, title: "My title", message: "Hello, world !"}
ikc3> id
1
ikc3> message
"Hello, world !"

You can use the dot syntax to access map values from a Sym key :

ikc3> a = %{id: 1, title: "My title", message: "Hello, world !"}
%{id: 1, title: "My title", message: "Hello, world !"}
ikc3> a.id
1
ikc3> a.message
"Hello, world !"

You can also use the KC3.access function for the same result :

ikc3> a = %{id: 1, title: "My title", message: "Hello, world !"}
%{id: 1, title: "My title", message: "Hello, world !"}
ikc3> access(a, :id)
1
ikc3> access(a, :message)
"Hello, world !"

Unicode characters

ikc3 fully supports Unicode :

Some unicode characters :

ikc3> '\U+1B2FB'
'𛋻'
ikc3> '𐅀'
'𐅀'
ikc3> '🤩'
'🤩'
ikc3>

Large integers

ikc3> a = 1 + 100000000000000000000000000000000
100000000000000000000000000000001
ikc3> a * a
10000000000000000000000000000000200000000000000000000000000000001
ikc3>

Ratios

Ratios are made with a couple of large integers : the numerator which can be any number, and the denominator which has to be positive. They represent fractions of integral numbers. They are written with a slash and no space.

ikc3> 1/2 + 2/3
7/6
ikc3> 1/2 * 2/3
1/3
ikc3> 1/2 / 2/3
3/4
ikc3> 1/2 - 2/3
-1/6

Complex numbers

Complex numbers are constructed using the operator +i on any kind of numbers (unsigned, signed, float, ratios, and even other complex numbers). For instance, you can write a +i b where a and b are real numbers.

ikc3> 1 +i 2
1 +i 2
ikc3> 1 +i 2 + 2 +i 3
3 +i 5
ikc3> (1 +i 2) * (2 +i 3)
-4 +i 7
ikc3> (1 +i 2) / (2 +i 3)
0 +i 0
ikc3> (1/1 +i 2/1) / (2 +i 3)
8/13 +i 1/13

As you can see integer division is not producing ratios. That might change in future releases.

Lists

Lists are marked with brackets [].

Regular lists can be :

Regular lists end with the empty list : [1] == [1 | []].

You can also contruct dotted lists like in Common Lisp where the next list pointer is an arbitrary form. E.g. :

All these list formats are supported in pattern matching.

Pattern matching and destructuring

The KC3 pattern matching principles come from Erlang and Elixir.

All tag data structures in KC3 can be pattern matched using the equal sign (=) against litteral values containing identifiers. All identifiers are supposed to be new bindings when using pattern matching in KC3. If you want to use an identifier's value in pattern matching you must use the pin operator (^). Variables can be assigned a new value from either side of the equal sign and from inside a tag data structure, which is called destructuring.

Examples :

ikc3> a = 1
1
ikc3> a = 2
2
ikc3> a
2
ikc3> ^ a = 1
void
ikc3> ^ a = 2
2
ikc3> ^ a = b
2
ikc3> b
2

To use destructuring just type the litteral value you want to match and put identifiers (variable names) where you want a variable matching the value on the other side of the equal sign. This is the most visual approach possible to text-based value matching : the data is constantly matched to litterals that show their type to the programmer. This is really helpful when writing large programs that need to scale in the way of abstractions. Let the data flow in the code through visual types.

Examples :

ikc3> [x, y | z] = List.reverse([1, 2, 3, 4])
[4, 3, 2, 1]
ikc3> x
4
ikc3> y
3
ikc3> z
[2, 1]

Macros

KC3 macros are like Common Lisp macros with Elixir pattern-matching.

Macros are like functions but start with macro instead of fn and their arguments do not get evaluated. However they get pattern matched and the full power of the pattern matcher is available for arguments destructuring. Use a map if you want named arguments. Use a list if you want &rest arguments, use a block if you want a &body argument.

When evaluated, a macro call returns a tag which is in turn evaluated in the calling site lexical environment. This allows for DSLs and custom control structures to be defined in KC3.

Many basic operations in KC3 are defined as macros : error handling, free operations with unwind-protect, graph database operations like Facts.with.

If, then, else.

Conditionals in KC3 are like in Ruby, for example :

ikc3> if true && true
ikc3>   1 + 1
ikc3>   2 + 2
ikc3> end
4

ikc3> if true && false
ikc3>   1 + 1
ikc3>   2 + 2
ikc3> else
ikc3>   3 + 3
ikc3>   4 + 4
ikc3> end
8

A KC3 if statement always return a value. If the condition is true, the first (then) block gets evaluated. If the condition is false the second block gets evaluated. If the condition is false and an else block is not provided, then void gets returned.

One liner examples with then :

ikc3> if 42 then 100 else 101 end
100
ikc3> if 0 then 100 else 101 end
101

defmodule and def

Example :

ikc3> defmodule Example do
ikc3>   def three = 3
ikc3>   def double = fn (x) do x * 2 end
ikc3>   def double_tuple = macro (x) do {x, x} end
ikc3>   def operator_double = %KC3.Operator{sym: :double, symbol_value: fn (x) { x * 2 }
ikc3> end
Example
ikc3> Example.three
3
ikc3> Example.double
fn (x) do x * 2 end
ikc3> Example.double(21)
42
ikc3> Example.double_tuple(:ok)
{:ok, :ok}
ikc3> double 21
42

Facts

The Facts module allows read and write access to a graph database containing facts : triples of subject, predicate, object.

Examples for querying the KC3 database containing all definitions of the interpreter :

ikc3> Facts.with_tags(Facts.env_facts(), KC3, :operator, ?,
        fn (fact) { puts(fact.object); :ok })
operator_eq
operator_gt
operator_lt
[...]
:ok

Top : KC3 Guides

Previous : KC3 Structure Guide