A bigger program
A web server in front of a stock room.
The program
The stock room is a process that owns the stock. It waits for a message, answers it, and calls itself with the new stock to wait for the next one. The server runs each connection in a process of its own, and those processes never touch the stock directly. They send the stock room a message and wait for its reply.
import scarlet/binary
import scarlet/http
import scarlet/http.{Get, Post, Request, Response}
import scarlet/map
import scarlet/process
import scarlet/process.{Subject}
type Ask {
Count(name String, reply Subject(Int))
Buy(name String, reply Subject(Result(Int, Nil)))
}
fn stockroom(inbox Subject(Ask), stock Map(String, Int)) Nil {
match process.receive(inbox) {
Count(name, reply) -> {
process.send(reply, map.get(stock, name) or 0)
stockroom(inbox, stock)
}
Buy(name, reply) -> match map.get(stock, name) or 0 {
0 -> {
process.send(reply, Err(Nil))
stockroom(inbox, stock)
}
n -> {
process.send(reply, Ok(n - 1))
stockroom(inbox, map.set(stock, name, n - 1))
}
}
}
}
fn open(stock Map(String, Int)) Subject(Ask) {
boot = process.subject()
_ = process.spawn(fn() {
inbox = process.subject()
process.send(boot, inbox)
stockroom(inbox, stock)
})
process.receive(boot)
}
fn ask(room Subject(Ask), message fn(Subject(a)) Ask) a {
reply = process.subject()
process.send(room, message(reply))
process.receive(reply)
}
fn route(room Subject(Ask), req Request) Response {
match (req.method, http.path(req)) {
(Get, <<'/stock/', name:binary>>) -> {
item = binary.to_string(name) or ''
n = ask(room, fn(reply) Count(item, reply))
http.text('${n} ${item} left')
}
(Post, <<'/buy/', name:binary>>) -> {
item = binary.to_string(name) or ''
match ask(room, fn(reply) Buy(item, reply)) {
Ok(n) -> http.text('sold, ${n} ${item} left')
Err(Nil) -> http.text('${item} is sold out')
}
}
_ -> http.not_found()
}
}
pub fn main() {
room = open(map.from_list([('tea', 2), ('jam', 0)]))
match http.serve('0.0.0.0', 8080, fn(req) route(room, req)) {
Ok(_) -> println('listening on http://localhost:8080')
Err(e) -> println('could not listen: ${e}')
}
}
Run it
scarlet run stock_server.scrl
Then, from another terminal:
$ curl localhost:8080/stock/tea
2 tea left
$ curl -X POST localhost:8080/buy/tea
sold, 1 tea left
$ curl -X POST localhost:8080/buy/tea
sold, 0 tea left
$ curl -X POST localhost:8080/buy/tea
tea is sold out