Last active
July 21, 2020 18:30
-
-
Save tgrospic/8ae9ac09d900d314f81753d30d71c411 to your computer and use it in GitHub Desktop.
Simple Rholang with higher-order channels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object rholang { | |
/** | |
* This interface represents terms in our Simple Rholang language. | |
* Everything is a process. | |
*/ | |
trait Process | |
/** | |
* AST of our Simple Rholang language. It's private and accessible only | |
* inside this object. The only way to change the state (Set[Process]) is to | |
* call defined operations (nil, par, send, receive). | |
*/ | |
private final case class Par(s: Set[Process] = Set()) extends Process { | |
override def toString: String = s"${s.mkString(" | ")}" | |
} | |
private final case class Send(name: Process) extends Process | |
private final case class Receive(name: Process) extends Process | |
/** | |
* `nil` and `par` are Monoid operations (empty, combine). | |
* | |
* All terms (processes) are collected in a Set defined in Par(Set[Process]). | |
*/ | |
def nil: Process = Par(s = Set()) | |
def par(p1: Process, p2: Process = nil): Process = (p1, p2) match { | |
// Combine states from both Par's | |
// This rule also "erase" `nil` from both sides of Par | |
// nil | p == p == p | nil | |
case (Par(s1), Par(s2)) => Par(s1 ++ s2) | |
// Next two cases flatten nested Par's | |
// Par(Set(Par(Send(A)))) => Par(Set(Send(A))) | |
case (Par(s1), p2) => Par(s1 + p2) | |
case (p1, Par(s2)) => Par(s2 + p1) | |
// All other cases just collect in a Set | |
case (p1, p2) => Par(Set(p1, p2)) | |
} | |
/** | |
* `send` and `receive` represent basic operations. | |
*/ | |
def send(name: Process): Process = par(Send(name)) | |
def receive(name: Process): Process = par(Receive(name)) | |
/** | |
* Support for ground types (built-in processes). | |
*/ | |
private trait Ground extends Process | |
private final case class GString(x: String) extends Ground { | |
override def toString: String = s""""${x}"""" | |
} | |
private final case class GInt(x: Int) extends Ground { | |
override def toString: String = s"${x}" | |
} | |
private final case class GSystem(id: String, proc: Process) extends Ground { | |
override def toString: String = s"SYS($id, $proc})" | |
} | |
private final case class GTuple(args: Seq[Process]) extends Ground { | |
override def toString: String = s"(${args.mkString(", ")})" | |
} | |
def string(x: String): Process = par(GString(x)) | |
def int(x: Int): Process = par(GInt(x)) | |
def sys(x: String, proc: Process): Process = par(GSystem(x, proc)) | |
def tuple(p1: Process, p2: Process): Process = par(GTuple(Seq(p1, p2))) | |
/** | |
* Converter to process | |
*/ | |
trait ToProcess[A] { | |
def convertToProcess(a: A): Process | |
} | |
// Helper if process cannot be converted automatically | |
def p[A](x: A)(implicit instance: ToProcess[A]): Process = instance.convertToProcess(x) | |
def par[A: ToProcess, B: ToProcess](x: A, y: B): Process = par(p(x), p(y)) | |
// Tuples defined as infix function e.g. (a ~ b) | |
def t[A: ToProcess, B: ToProcess](x1: A, x2: B): Process = tuple(p(x1), p(x2)) | |
// Extensions | |
def send[A: ToProcess](x: A): Process = send(p(x)) | |
def receive[A: ToProcess](x: A): Process = receive(p(x)) | |
def sys[A: ToProcess](id: String, proc: A): Process = sys(id, p(proc)) | |
/** | |
* Reductions of our super Simple Rholang language. | |
* When matching Send/Receive are found they are erased. | |
*/ | |
def eval(proc: Process): Process = | |
proc match { | |
case Par(state) => | |
val procs = state.foldLeft(Set[Process]()) { | |
// Reduction of Send if matches Receive | |
case (acc, send @ Send(name)) => | |
if (state.contains(Receive(name))) acc else acc + send | |
// Reduction of Receive if matches Send | |
case (acc, rec @ Receive(name)) => | |
if (state.contains(Send(name))) acc | |
else acc + rec | |
// System processes | |
case (acc, GSystem(id, arg)) if id == "rho:io:stdout" => | |
// Standard output | |
println(s"> ${arg}") | |
acc | |
// For all other combinations we do nothing | |
case (acc, p) => acc + p | |
} | |
Par(procs) | |
case p => p | |
} | |
} | |
import rholang._ | |
// Implicit conversion for Process (identity) | |
implicit object ProcessIdentity extends ToProcess[Process] { | |
def convertToProcess(x: Process): Process = x | |
} | |
// Implicit conversion for String | |
implicit object StringToProcess extends ToProcess[String] { | |
def convertToProcess(x: String): Process = string(x) | |
} | |
// Implicit conversion for Int | |
implicit object IntToProcess extends ToProcess[Int] { | |
def convertToProcess(x: Int): Process = int(x) | |
} | |
// Binary infix functions | |
implicit class syntaxRholang[A](private val p1: A) extends AnyVal { | |
// Defines pipe `|` as nicer syntax for `par` | |
// `|` is not the best choice, already defined for numbers. `||` maybe? | |
// a | b ==> par(a, b) | |
// a | b | c ==> par(par(a, b), c) | |
def |[B: ToProcess](p2: B)(implicit to: ToProcess[A]): Process = par(p1, p2) | |
// Defines tilde `~` as nicer syntax for tuple (how to lift Scala tuple?) | |
// a ~ b ==> t(a, b) | |
// a ~ b ~ c ==> t(t(a, b), c) | |
def ~[B: ToProcess](p2: B)(implicit to: ToProcess[A]): Process = t(p1, p2) | |
} | |
/** | |
* Our first Simple Rholang program. | |
*/ | |
val prog1: Process = { | |
val p1 = send(nil | 42 | "A") | |
val p2 = send("B") | |
val p3 = receive("B") | |
val p4 = send("C") | |
val p5 = sys("rho:io:stdout", "Special channel!" ~ 42) | |
val p6 = receive("A" | 42) | |
p1 | p2 | p3 | p4 | p5 | p6 | nil | |
} | |
// prog1: rholang.Process = Send("B") | Receive("B") | Send("C") | Send(42 | "A") | Receive("A" | 42) | SYS(rho:io:stdout, ("Special channel!", 42)}) | |
val prog2 = "Process String in par with Int" | 42 | |
// prog2: rholang.Process = "Process String in par with Int" | 42 | |
/** | |
* Evaluate the program and get remaining processes (state inside Par). | |
*/ | |
val res = eval(prog1) | |
// > ("Special channel!", 42) | |
// res: rholang.Process = Send("C") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment