Skip to content

Commit

Permalink
monadic
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbert van Dalen committed Feb 17, 2021
1 parent 451af2f commit c419692
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.metals/*
.bloop/*
.vscode
.bsp
.scalafmt.conf
native
project/target/*
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.2.8
sbt.version = 1.4.6
109 changes: 109 additions & 0 deletions src/main/scala/net/manikin/main/Main2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package net.manikin.main

object Main2 {

trait Id[+O] {
def ini: O
}


trait Msg[-I <: Id[O], O, +R, C <: Context[C]] {
def pre: Self[I, O, C] => Boolean
def app: O => O
def eff: Self[I, O, C] => Result[R, C]
def pst: Self[I, O, C] => Boolean
}

trait Context[C <: Context[C]] {
def prev: C
def apply[O](id: Id[O]): O
def send[I <: Id[O], O, R](id: I, msg: Msg[I, O, R, C]): Result[R, C]
}

case class Self[+I <: Id[O], O, C <: Context[C]](self: I, context: C) {
def apply(): O = context(self)
def prev(): O = context.prev(self)
def apply[O2](id: Id[O2]): O2 = context(id)
def prev[O2](id: Id[O2]): O2 = context.prev(id)
def send[I2 <: Id[O2], O2, R](id: I2, msg: Msg[I2, O2, R, C]): Result[R, C] = context.send(id, msg)
}

case class Result[+R, C <: Context[C]](result: R, context: C) {
def apply(): R = result
def apply[O2](id: Id[O2]): O2 = context(id)
def prev[O2](id: Id[O2]): O2 = context.prev(id)
def send[I2 <: Id[O2], O2, R2](id: I2, msg: Msg[I2, O2, R2, C]): Result[R2, C] = context.send(id, msg)
}


case class SimpleContext(prev: SimpleContext = null, state: Map[Id[_], _] = Map()) extends Context[SimpleContext] {
def previous = prev
def apply[O](id: Id[O]): O = state.getOrElse(id, id.ini).asInstanceOf[O]
def send[I <: Id[O], O, R](id: I, msg: Msg[I, O, R, SimpleContext]): Result[R, SimpleContext] = {
if (!msg.pre(Self(id, this))) throw sys.error("Pre-condition failed")
else {
val eff = msg.eff(Self(id, SimpleContext(this, state + (id -> msg.app(apply(id))))))
if (msg.pst(Self(id, SimpleContext(this, eff.context.state)))) eff
else throw sys.error("Post-condition failed")
}
}
}

trait SMsg[-I <: Id[O], O] extends Msg[I, O, Unit, SimpleContext]
def none[I2 <: Id[O2], O2, C <: Context[C]] = (c: Self[I2, O2, C]) => Result[Unit, C]((), c.context)

case class AccountId(IBAN: String) extends Id[Account] {
def ini = Account()
}

case class Account(balance: Double = 0.0)

case class Open(init: Double) extends SMsg[AccountId, Account] {
def pre = _ => init > 0.0
def app = _ => Account(init)
def eff = none
def pst = c => c().balance == init
}

case class Withdraw(amount: Double) extends SMsg[AccountId, Account] {
def pre = c => amount > 0.0 && c().balance >= amount
def app = o => Account(o.balance - amount)
def eff = none
def pst = c => c().balance == c.prev().balance - amount
}

case class Deposit(amount: Double) extends SMsg[AccountId, Account] {
def pre = _ => amount > 0.0
def app = o => Account(o.balance + amount)
def eff = none
def pst = c => c().balance == c.prev().balance + amount
}

case class TransferId(id: Long) extends Id[Transfer] {
def ini = Transfer()
}

case class Transfer(from: AccountId = null, to: AccountId = null, amount: Double = 0.0)

case class Book(from: AccountId, to: AccountId, amount: Double) extends SMsg[TransferId, Transfer] {
def pre = _ => amount >= 0.0
def app = _ => Transfer(from, to, amount)
def eff = c => c.send(from, Withdraw(amount)).send(to, Deposit(amount))
def pst = c => c(from).balance + c(to).balance == c.prev(from).balance + c.prev(to).balance
}

def main(args: Array[String]): Unit = {
val c = SimpleContext()
val a1 = AccountId("A1")
val a2 = AccountId("A2")
val t1 = TransferId(1)

val c2 = c.
send(a1, Open(50)).
send(a2, Open(80)).
send(t1, Book(a1, a2, 30))

println("c2: " + c2.context.state)

}
}

0 comments on commit c419692

Please sign in to comment.