Skip to content

Instantly share code, notes, and snippets.

@vil1
Last active December 1, 2016 11:49
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vil1/073c1db241dcb4a6ca6c5992c0c57a3e to your computer and use it in GitHub Desktop.
Save vil1/073c1db241dcb4a6ca6c5992c0c57a3e to your computer and use it in GitHub Desktop.
A type level state machine, whose definition is statically checked to be deterministic (material related to http://kanaka.io/blog/2016/04/25/interlude-typelevel-state-machine.html)
import shapeless._, labelled._, ops.record._
import scala.annotation.implicitNotFound
import scala.language.dynamics
class Transition[In, Out]
class Definition[R <: HList]
class StateMachine[S] extends Dynamic {
def selectDynamic[R <: HList, O, T](trans: Witness)
(implicit d: Definition[R],
select: Selector.Aux[R, trans.T, T],
unpack: Unpack2[T, Transition, S, O]
): StateMachine[O] = new StateMachine[O]
}
object StateMachine {
def initial[S] = new StateMachine[S]
def definition[R <: HList : IsDeterministic](r: R) = new Definition[R]
def transition[I, O] = new Transition[I, O]
}
// Typeclass witnessing that if F is of shape FieldType[K, Transition[I, O1]]
// there is no field of type FieldType[K, Transition[I, O2]] in record R
class IsNotMemberOf[F, R <: HList]
object IsNotMemberOf {
def apply[F, L <: HList](f: F, l: L)(implicit isNotMemberOf: IsNotMemberOf[F, L]) = true
implicit def hnilIsUnique[F] = new IsNotMemberOf[F, HNil]
implicit def hconsIsUnique1[K, I, O, H, T <: HList, In, Out]
(implicit unpackV: Unpack2[H, Transition, In, Out],
ev : In =:!= I,
notMemberOfTail: IsNotMemberOf[FieldType[K, Transition[I, O]], T]
) = new IsNotMemberOf[FieldType[K, Transition[I, O]], FieldType[K, H] :: T]
implicit def hconsIsUnique2[K,V, K2, H, T <: HList]
(implicit ev : K =:!= K2,
notMemberOfTail: IsNotMemberOf[FieldType[K, V], T]
) = new IsNotMemberOf[FieldType[K, V], FieldType[K2, H] :: T]
}
@implicitNotFound(msg = "The provided definition is not deterministic, " +
"it contains at least two transitions with the same label starting from the same state")
class IsDeterministic[R <: HList]
object IsDeterministic {
implicit def hnilIsDeterministic = new IsDeterministic[HNil]
implicit def hconsIsDeterministic[H, T <: HList]
(implicit headIsNotMemberOfTail: IsNotMemberOf[H, T],
tailIsDeterministic : IsDeterministic[T]
) = new IsDeterministic[H :: T]
}
object Door {
import StateMachine._
import syntax.singleton._
trait Open
trait Closed
trait Locked
implicit val transitions = definition(
"open" ->> transition[Closed, Open] ::
"close" ->> transition[Open, Closed] ::
"lock" ->> transition[Closed, Locked] ::
"unlock" ->> transition[Locked, Closed] ::
HNil
)
// non-deterministic definition, doesn't compile
/*
implicit val transitions = Definition(
("open" ->> Transition[Closed, Open]) ::
("close" ->> Transition[Open, Closed]) ::
("lock" ->> Transition[Closed, Locked]) ::
("unlock" ->> Transition[Locked, Closed]) ::
("unlock" ->> Transition[Locked, Open]) ::
HNil
)
*/
val init = initial[Open]
// compiles
init.close.lock.unlock.open
// doesn't compile
// init.open
// init.close.close
// init.close.lock.open
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment