Skip to content

Instantly share code, notes, and snippets.

@slavaschmidt
Created April 13, 2016 19:22
Show Gist options
  • Save slavaschmidt/5314e87389945b60620e4b49160431c3 to your computer and use it in GitHub Desktop.
Save slavaschmidt/5314e87389945b60620e4b49160431c3 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)
}
@nmcb
Copy link

nmcb commented Apr 17, 2016

@slavaschmidt ValentinKasas would like to see the code, requests at twitter. Make this gist public?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment