Skip to content

Instantly share code, notes, and snippets.

@jimleroyer
Last active September 26, 2017 22:38
Show Gist options
  • Save jimleroyer/17227b7e334d020a21deb36086b9b978 to your computer and use it in GitHub Desktop.
Save jimleroyer/17227b7e334d020a21deb36086b9b978 to your computer and use it in GitHub Desktop.
Scala Workflow using existential trait, type converters and implicits
import scala.language.existentials
// Simple laboratory exercise that makes use of a [[TransitionExistential]].
// The existential type hides its type parameters and provides transition
// values and parameters' conversions.
object Lab {
/** ************** Types */
// Type class that will provide the state value automatically.
sealed trait StateValue[V] {
def value[T](any: T): V
}
object StateValue {
def apply[A](implicit ev: StateValue[A]): StateValue[A] = ev
implicit val identityInstance: StateValue[Any] = new StateValue[Any] {
override def value[T](v: T): Any = v.asInstanceOf[Any]
}
implicit val imageInstance: StateValue[Image] = new StateValue[Image] {
// override def value(v: Image): Image = identity(v)
override def value[T](v: T): Image = v.asInstanceOf[Image]
}
implicit val createImageInstance: StateValue[CreateImage] = new StateValue[CreateImage] {
// override def value(v: CreateImage): CreateImage = identity(v)
override def value[T](v: T): CreateImage = v.asInstanceOf[CreateImage]
}
}
trait TransitionExistential {
type Param
type Return
val transition: Transition[Param, Return]
val evidenceParam: StateValue[Param]
val evidenceReturn: StateValue[Return]
}
trait StateDef {
def id: String
}
case class State[CV](override val id: String, context: CV) extends StateDef
trait Action[-P, +R]
abstract class Condition[-T] extends Function1[T, Boolean] {
override def apply(contextState: T): Boolean
}
case class Transition[-P, +R](tailState: StateDef, headState: StateDef, action: Action[P, R], condition: Option[Condition[P]] = None)
case class Workflow private(transitions: List[TransitionExistential]) {
def getTransitions(tail: StateDef, head: StateDef): List[TransitionExistential] = {
transitions.filter { t =>
t.transition.tailState.id == tail.id && t.transition.headState.id == head.id
}
}
def matchTransition[P](tail: State[P], head: StateDef): Option[TransitionExistential] = {
val existentials = getTransitions(tail, head)
existentials.find { e =>
val t = e.transition
val value = e.evidenceParam.value(tail.context)
t.condition.forall(cond => cond(value))
}
}
}
/** ************** Fixtures */
// Model
case class CreateImage()
case class Image()
// States
class Init extends State[CreateImage]("INIT", CreateImage())
val init = new Init()
case object Published extends StateDef {
override def id: String = "PUBLISHED"
}
// Factories for building up existentials out of concrete values.
case class TransitionFactory[P, R](override val transition: Transition[P, R])
(implicit val evidenceParam: StateValue[P],
implicit val evidenceReturn: StateValue[R]) extends TransitionExistential {
type Param = P
type Return = R
}
// Action
class CreateTipAction extends Action[CreateImage, Image]
// class CreateTipAction extends Action[WorkflowModel, WorkflowModel]
class InitAction extends Action[Unit, CreateImage]
/** ************** Demo */
val transitions = List(
TransitionFactory(Transition(init, Published, new CreateTipAction()))
)
val w = Workflow(transitions)
val t = w.matchTransition(init, Published)
Set("1", "2").toList
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment