Lake

Machines

A machine is the core abstraction in Lake. Every machine call spawns a new cooperatively-scheduled process.

§Defining a machine

lake
name is {
  # branches go here
}

The is keyword separates the machine name from its body. The body is enclosed in curly braces and contains one or more branches.

§A simple machine

A counter that decrements its argument until it reaches zero, then prints done.

@rt(rt_write)

counter is {
  0 i64 -> { rt_write(1 "done\n" 5) }
  n i64 -> { self(n-1) }
}

main is {
  _ -> {
    counter(5)
  }
}

When main calls counter(5), a new process is spawned in the counter machine. The branch with literal guard 0 i64 does not match (the argument is 5), so the branch with the catch-all n i64 runs and the process transitions with self(n-1). After five iterations the literal guard matches and done is written.

§Concurrent execution

Calling a machine always spawns a new process. Multiple spawns produce concurrent processes managed by the scheduler.

@rt(rt_write)

worker is {
  0 i64 -> { rt_write(1 ".\n" 2) }
  n i64 -> { self(n-1) }
}

main is {
  _ -> {
    worker(2000)
    worker(2000)
    worker(2000)
    worker(2000)
  }
}

main spawns four workers. Each runs a quantum of 256 reductions before yielding to the next. You will see four dots, one per worker, in some interleaved order.

§Ping-pong

Machines can spawn each other.

@rt(rt_write)

pong is {
  _ -> {
    rt_write(1 "pong\n" 5)
  }
}

ping is {
  _ -> {
    rt_write(1 "ping\n" 5)
    pong()
  }
}

main is {
  _ -> {
    ping()
    ping()
    ping()
  }
}

main spawns three ping processes. Each ping writes a line and then spawns a pong.

§Declaration order

Machines may be declared in any order. Forward references work.

lake
main is {
  _ -> {
    counter(10)
  }
}

counter is {
  0 i64 -> { rt_write(1 "done\n" 5) }
  n i64 -> { self(n-1) }
}