Koka language

bearophile bearophileHUGS at lycos.com
Fri Aug 17 16:11:23 PDT 2012


Through Reddit I have found this new little language, that 
probably is meant to be just an experiment:

Reddit thread:
http://www.reddit.com/r/programming/comments/ycz6a/koka_a_function_oriented_language_with_effect/

LtU thread:
http://lambda-the-ultimate.org/node/4589

Slides:
http://research.microsoft.com/en-us/projects/koka/2012-overviewkoka.pdf

Little tutorial:
http://www.rise4fun.com/koka/tutorial

To see a bit larger examples:
http://www.rise4fun.com/Koka/


Koka is a function-oriented language, this means it's almost a 
functional language, with a syntax a bit similar to JavaScript, 
probably because today lot of programmers are used to JS syntax. 
Kika has kind of module-level type inference.

But the most important characteristic of Koka is its "effect 
inference". The effects are like the side effects prevented by 
the "pure" or "nothrow" of D. Koka is able to infer not just the 
plain types like int, list<int> or generic types like list<a>, 
but it's also able to infer the various effects of functions, 
avoiding the need to write them manually, see below.

Each Koka experession has associated its type, plus something 
like <div,exn,ndet>, this is a set of the effects of that 
expression. In D we do the opposite, we annote what effects a 
function doesn't have, and they are not organized in a set (the 
use of such sets offers significant advantages explained below).

Below there are some quotations from the slides, tutorial and the 
LtU thread. Fisrt I show some simple features , and later there 
are notes about its type system.

-----------------------

Dot notation, nice for chaining (this is the same as in D):

s.map(f)  ==  map(s, f)
s.caesar.map(to-upper).print

-----------------------

Optional parameters have a default value:

function sublist(xs : list<a>, start : int,
                  len : int = xs.length ) : list<a>
{
   if (start <= 0) return xs.take(len)
   match(xs) {
     nil -> nil
     cons(_,xx) -> xx.sublist(start-1, len)
   }
}

-----------------------

Named parameters can be used in any order:

function world() {
   "hi world".substring(len=5, start=3)
}

-----------------------

Structs are immutable by default: changing a field usually copies 
a struct:

function birthday(p : person) {
    p(age = p.age + 1)
}


Where structs are nested you use something like:

p(address = p.address(street = "Madison Sq"))

-----------------------

A mutable example (uses immutable references to the values, so 
it's similar to OcaML):

fun fib2(n)
{
   var x := 0
   var y := 1
   repeat(n) {
     y0 = y
     y := x+y
     x := y0
   }
   return x
}

The var declaration declares a variable that can be assigned too 
using the (:=) operator. In contrast, a regular equality sign, as 
in y0 = y introduces an immutable value y0. For clarity, one can 
actually write val y0 = y for such declaration too but we usually 
leave out the val keyword.

-----------------------

When we look at the types inferred for references, we see that x 
and y have type ref<h,int> which stands for a reference to a 
mutable value of type int in some heap h. The effects on heaps 
are allocation as heap<h>, reading from a heap as read<h> and 
writing to a heap as write<h>. The combination of these effects 
is called stateful and denoted with the alias st<h>.

-----------------------

A novel part about Koka is that it automatically infers all the 
side effects that occur in a function:

- The absence of any effect is denoted as "total" (or <>) and 
corresponds to pure mathematical functions.
- If a function can raise an exception the effect is "exn";
- if a function may not terminate the effect is "div" (for 
divergence);
- The combination of "exn" and "div" is "pure" and corresponds 
directly to Haskell's notion of purity;
- Non-deterministic functions get the "ndet" effect.
- The 'worst' effect is "io" and means that a program can raise 
exceptions, not terminate, be non-deterministic, read and write 
to the heap, and do any input/output operations.

-----------------------

Here are some examples of effectful functions:

function square1(x : int) : total int
{
   return x * x
}

function square2(x : int) : io int
{
   print( "a not so secret side-effect" )
   return x * x
}

function square3(x : int) : div int
{
   square3(x)
   return x * x
}

function square4(x : int) : exn int
{
   error("oops")
   return x * x
}

-----------------------

Often, a function contains multiple effects, for example:

function combine-effects()
{
   i = random-int()  // non-deterministic
   error("hi")       // exception raising
   combine-effects() // and non-terminating
}

The effect assigned to combine-effects are ndet, div, and exn. We 
can write such combination as a row of effects as <div,exn,ndet>. 
When you hover over the combine-effects identifiers, you will see 
that the type inferred is really <pure,ndet> where pure is a type 
alias defined as

alias pure = <div,exn>

-----------------------

(*)   : (int,int)    -> total int
(/)   : (int,int)    -> exn int
exec  : (turingMach) -> div bool
(!)   : (ref<h,a>)   -> read<h> a
print : (string)     -> io ()


Our approach: row-polymorphism, we use a set of effect labels:
- The empty effect is <> (total)
- An effect can be extended as <div|e>
- Sugar: <div,exn> == <div|<exn|<>>>
- Duplicates are ok: <div|<div|e>> == <div|e>

type ndet: !
type exn : !
type div : !
type rd  : !
type wr  : !
type heap<h> : H -> !

alias pure     = <div,exn>
alias read<h>  = <rd,heap<h>>
alias write<h> = <wr,heap<h>>
alias st<h>    = <rd,wr,heap<h>>
alias io       = <st<global>,div,exn,ndet>

-----------------------

When the effect is total we usually leave it out in the type 
annotation. [...] Sometimes, we write an effectful function, but 
are not interested in explicitly writing down its effect type. In 
that case, we can use a wildcard type which stands for some 
inferred type. A wildcard type is denoted by writing an 
identifier prefixed with an underscore, or even just an 
underscore by itself:

function square6( x : int ) : _e int
{
   print("I did not want to write down the io effect")
   return x*x
}

-----------------------

In D there is nothing like the "div" (or @nodiverge, to keep the 
style of the negative D annotations) annotation. Maybe it's hard 
to enforce this effect in a mostly imperative language like D, I 
don't know.

On LtU he/they explains why the "div" (divergence) annotation 
type is useful:

>The presence of divergence complicates reasoning about the 
>program. For example, a piece of code on which no other code has 
>data dependencies upon can be discarded without affecting 
>referential transparency, under some restrictions on its side 
>effects. Of course, diverging code should not be discarded. 
>Keeping track of an upper-bound on divergence allows such 
>reasoning, e.g. in an optimizing compiler. While not being a 
>tight upper bound (which is undecidable anyway), such inference 
>is an easy bound. I conjecture a lot of basic pattern matching 
>code fits this criteria (and is probably the code we want to 
>optimise away too). However, I have no hard evidence supporting 
>this conjecture, nor know of studies examining this question. 
>Another reason for tracking divergence is suitability to run on 
>different architectures. The application I refer to is database 
>queries, where recursive code cannot be run directly on the DB 
>engine, even if it does terminate. (The system I saw this in was 
>Wadler&co.'s Links web programming language.) We believe that 
>this semantic correspondence is the true power of full effect 
>types and it enables effective equational reasoning about the 
>code by a programmer. For almost all other existing programming 
>languages, even the most basic semantics immediately include 
>complex effects like heap manipulation and divergence. In 
>contrast, Koka allows a layered semantics where we can easily 
>separate out nicely behaved parts, which is essential for many 
>domains, like safe LINQ queries, parallel tasks, tier-splitting, 
>sand-boxed mobile code, etc.<

-----------------------

The effects analysis works with polymorphic functions too:

Polymorphic effects

Many functions are polymorphic in their effect. For example, the 
map function applies a function f to each element of a (finite) 
list. As such, the effect depends on the effect of f, and the 
type of map becomes:

load in editormap : (xs : list<a>, f : (a) -> e b) -> e list<b>

We use single letters (possibly followed by digits) for 
polymorphic types. Here, the map functions takes a list with 
elements of some type a, and a function f that takes an element 
of type a and returns a new element of type b. The final result 
is a list with elements of type b. Moreover, the effect of the 
applied function e is also the effect of the map function itself; 
indeed, this function has no other effect by itself since it does 
not diverge, nor raises exceptions.

We can use the notation <l|e> to extend an effect e with another 
effect l. This is used for example in the while function which 
has type:

load in editorwhile : ( pred : () -> <div|e> bool, action : () -> 
<div|e> () ) -> <div|e> ()

The while function takes a predicate function and an action to 
perform, both with effect <div|e>. The final effect of the while 
function is of course also <div|e>. Indeed, since while may 
diverge depending on the predicate its effect must include 
possible divergence

The reader may be worried that the type of while forces the 
predicate and action to have exactly the same effect <div|e>, 
which even includes divergence. However, when effects are 
inferred at the call-site, both the effects of predicate and 
action are extended automatically until they match. This ensures 
we take the union of the effects in the predicate and action. 
Take for example the following loop

load in editorfunction looptest()
{
   while { odd(random-int()) }
   {
     error("<b>")
   }
}

In the above program, Koka infers that the predicate 
odd(random-int()) has effect <ndet|e1> while the action has 
effect <exn|e2> for some e1 and e2. When applying while, those 
effects are unified to the type <exn|ndet|div|e3> for some e3 
(which can be seen by hovering over the looptest identifier)

Bye,
bearophile


More information about the Digitalmars-d mailing list