Skip to content

Instantly share code, notes, and snippets.

@nmcb
Forked from slavaschmidt/SessionTypes.scala
Created June 21, 2016 14:34
Show Gist options
  • Save nmcb/2a3e1b0c6fc011248cef7022e53ef479 to your computer and use it in GitHub Desktop.
Save nmcb/2a3e1b0c6fc011248cef7022e53ef479 to your computer and use it in GitHub Desktop.
object SessionTypes {
import scala.language.higherKinds
import scala.language.implicitConversions
type Receiver = String
type Address = String
type Content = String
case class Parcel[+S<:State](content: Content, receiver: Receiver, address: Address)
sealed trait State
case class Packaged (byWhom: String) extends State
case class Sended (whoAccepts: String) extends State
case class Received (whoDelivers: String) extends State
case class Delivered(toWhom: String) extends State
case class Lost (witness: String) extends State
case class Moving (tracking: Long ) extends State
case class Destroyed(when: Long ) extends State
@annotation.implicitNotFound(msg = "state transition from ${A} to ${B} is not allowed")
sealed abstract class ~>[-A,+B]
implicit case object GoToThePost extends ~>[Packaged,Sended]
implicit case object AcceptFromSender extends ~>[Sended,Moving]
implicit case object BringToTheAddress extends ~>[Moving,Received]
implicit case object GiveToReceivcer extends ~>[Received,Delivered]
implicit case object HaveFun extends ~>[Moving,Destroyed]
implicit case object DrinkAlcohol extends ~>[Moving,Lost]
implicit case object StopDrinking extends ~>[Lost,Moving]
trait Session[S] {
type Self = S
type Dual
type DualOf[D] = Session[Self] { type Dual = D }
def run (self: Self, dual: Dual): Unit
}
def runSession[AS,D:Session[AS]#DualOf](session: AS, dual: D) =
implicitly[Session[AS]#DualOf[D]].run(session, dual)
case class Stop(msg: String)
case class In [ R[S<:State],A<:State,B<:State,+C](recv: R[A] => (C,R[B]))(implicit stateTransition: ~>[A,B])
case class Out[+R[S<:State],A<:State,+C](data: R[A], cont: C)
implicit object StopDual extends Session[Stop] {
type Dual = Stop
def run (self: Self, dual: Dual): Unit = {}
}
implicit def InDual[R[S<:State],RA<:State,RB<:State,C](implicit cont: Session[C]) = new Session[In[R,RA,RB,C]] {
type Dual = Out[R,RA,cont.Dual]
def run(self: Self, dual: Dual) = cont.run(self.recv(dual.data)._1, dual.cont)
}
implicit def OutDual[R[S<:State],RA<:State,RB<:State,C](implicit cont: Session[C]) = new Session[Out[R,RA,C]] {
type Dual = In[R,RA,RB,cont.Dual]
def run(self: Self, dual: Dual) = cont.run(self.cont, dual.recv(self.data)._1)
}
def trivialServer = Stop("Done")
def trivialClient = Stop("Me too")
def trivial = runSession(trivialServer, trivialClient)
def server =
In { p: Parcel[Packaged] =>
val content = "Changed by server " + p.content
val result = Parcel[Sended](content, p.receiver, p.address)
val stop = Stop("With server result: " + result)
(stop, result)
}
def client = {
val parcel = Parcel[Packaged]("Content", "Receiver", "Address")
val finish = Stop("With client result: " + parcel)
Out(parcel, finish)
}
def doServer =
In { p: Parcel[Packaged] =>
val result = Parcel[Sended]("Changed by server " + p.content, p.receiver, p.address)
(In { r: Parcel[Packaged] =>
println("Server result: " + result)
val stop = Stop("With server result: " + result)
val wrongOut = Out(result, stop)
(stop, result)
}, result)
}
def doClient = {
val first = Parcel[Packaged]("First", "Receiver", "Address")
val second = Parcel[Packaged]("Second", "Receiver", "Address")
def finish = {
println("Client results: " + first + " and " + second)
Stop("With client result: " + first)
}
Out(first, Out(second, finish))
}
def run = runSession(server, client)
def doRun = runSession(doServer, doClient)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment