object Sessions {
trait Session[This] {
type Dual
type HasDual[D] = Session[This]{type Dual=D}
def run(p: This, dp: Dual): Unit
}
implicit object StopSession extends Session[Stop] {
type Dual = Stop
def run(p: Stop, dp: Stop): Unit = {}
}
implicit def InDual[A, B](implicit sessionDIn: Session[B]): Sessions.Session[Sessions.In[A,B]]{type Dual = Sessions.Out[A,sessionDIn.Dual]} =
new Session[In[A, B]] {
type Dual = Out[A, sessionDIn.Dual]
def run(p: In[A, B], dp: Dual): Unit =
sessionDIn.run(p.func(dp.x), dp.y)
}
implicit def OutDual[A, B](implicit sessionDOut: Session[B]): Sessions.Session[Sessions.Out[A,B]]{type Dual = Sessions.In[A,sessionDOut.Dual]} =
new Session[Out[A, B]] {
type Dual = In[A, sessionDOut.Dual]
def run(p: Out[A, B], dp: Dual): Unit =
sessionDOut.run(p.y, dp.func(p.x))
}
sealed case class Stop()
sealed case class In[-A, +B](func: A => B)
sealed case class Out[+A, +B](x: A, y: B)
def addServer =
In{x: Int =>
In{y: Int => System.out.println("Thinking")
Out(x + y,
Stop())}}
def addClient =
Out(3,
Out(4, { System.out.println("Waiting")
In{z: Int => System.out.println(z)
Stop()}}))
def runSession[S, D: Session[S]#HasDual](p: S, dp: D) =
implicitly[Session[S]#HasDual[D]].run(p, dp)
// def runSession[S, D](p: S, dp: D)(implicit s: Session[S]#HasDual[D]) =
// s.run(p, dp)
//
// def runSession[S, D](p: S, dp: D)(implicit s: Session[S]{type Dual=D}) =
// s.run(p, dp)
// TODO: can we relax the ordering restrictions on dependencies so that we can use
// def runSession[S](p: S, dp: s.Dual)(implicit s: Session[S]) =
// s.run(p, dp)
// to emphasise similarity of type parameters and implicit arguments:
// def runSession[S][val s: Session[S]](p: S, dp: s.Dual) =
// s.run(p, dp)
def myRun = runSession(addServer, addClient)
}