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]) = 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]) = 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) }