Skip to content

tomjschuster/gleam_universal_server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

universal_server

test workflow

A Gleam implementation of the "Universal Server" demonstrated by Joe Armstrong in his blog post My favorite Erlang Program.

import gleam/erlang/process.{type Subject}
import gleam/result.{try}
import universal_server

pub fn main() {
  use u_subject <- try(universal_server.start(True))
  use f_subject <- try(universal_server.become(u_subject, factorial_server))

  let u_pid = process.subject_owner(u_subject)
  let f_pid = process.subject_owner(f_subject)
  let assert True = u_pid == f_pid

  let assert Ok(120) = compute(f_subject, 5)
  let assert Ok(3_628_800) = compute(f_subject, 10)

  Ok(Nil)
}

fn factorial_server(subject: Subject(#(Subject(Int), Int))) {
  let #(reply_to, n) =
    process.new_selector()
    |> process.selecting(subject, fn(m) { m })
    |> process.select_forever()

  process.send(reply_to, factorial(n))

  factorial_server(subject)
}

fn factorial(n: Int) -> Int {
  case n {
    0 -> 1
    n if n > 0 -> n * factorial(n - 1)
    _ -> panic as "cannot calculate negative factorial"
  }
}

fn compute(subject: Subject(#(Subject(Int), Int)), n: Int) -> Result(Int, Nil) {
  let reply_to = process.new_subject()
  process.send(subject, #(reply_to, n))
  process.receive(reply_to, 5000)
}

Development

gleam test  # Run the tests
gleam shell # Run an Erlang shell