Skip to content

Instantly share code, notes, and snippets.

@arturopala
Last active April 22, 2020 14:11
Show Gist options
  • Save arturopala/862c1879ab85626aecd7135358874cd9 to your computer and use it in GitHub Desktop.
Save arturopala/862c1879ab85626aecd7135358874cd9 to your computer and use it in GitHub Desktop.
Type safe action chain ADT
sealed trait Direction
sealed trait East extends Direction
case object East extends East
sealed trait West extends Direction
case object West extends West
sealed trait North extends Direction
case object North extends North
sealed trait South extends Direction
case object South extends South
sealed trait State
trait Idle extends State
trait Busy extends State
trait Facing[+D <: Direction] extends Busy
case class Moving[+D <: Direction](direction: D) extends Facing[D]
sealed trait Action[-B, +A]
sealed trait SimpleAction[-B, +A] extends Action[B, A]
case class Start[D <: Direction](direction: D)
extends SimpleAction[Idle, Facing[D]]
case class Move[D <: Direction](distance: Int)
extends SimpleAction[Facing[D], Moving[D]]
case class Rotate[D1 <: Direction, D2 <: Direction](direction: D2)
extends SimpleAction[Facing[D1], Facing[D2]]
case class Stop[D <: Direction](duration: String)
extends SimpleAction[Moving[D], Facing[D]]
case object End extends SimpleAction[Busy, Idle]
// actions can be chained into a composite action, formulated as a linked list
sealed trait Chain[-B, +A] extends Action[B, A]
// check is an empty chain element, use it to further restrict chain's entry state
case class Check[C]() extends Chain[C, C]
case class Step[C, B, A](before: Chain[C, B], action: Action[B, A])
extends Chain[C, A]
implicit class ActionExt[C, B](before: Action[C, B]) {
def ~>[A](action: Action[B, A]): Chain[C, A] = before match {
case chain: Chain[C, B] => Step(chain, action)
case _ => Step(Step(Check[C](), before), action)
}
}
val start = Start(West) ~> Move(5) ~> Rotate(South) ~> Move(10)
def continue[D <: Direction] =
Check[Facing[D]] ~> Move(1) ~> Rotate(East) ~> Move(2) ~> Rotate(West) ~> Move(3) ~> Rotate(West) ~> Move(4)
val continueWest = Check[Moving[West]] ~> Move(10) ~> Stop("30s") ~> Move(5)
val chainExample1 = start ~> continue ~> End
println(label(chainExample1))
val chainExample2 = start ~> continue ~> continueWest ~> continue ~> Rotate(East) ~> continue ~> End
println(label(chainExample2))
val chainExample3 = start ~> Move(100) ~> End
println(label(chainExample3))
val chainExample4 = start ~> Rotate(East) ~> End
println(label(chainExample4))
def label[B, A](action: Action[B, A]): String =
label(Nil, List(action)).mkString("\n")
@scala.annotation.tailrec
def label(acc: List[String], queue: List[Action[_, _]]): List[String] =
queue match {
case Nil => acc
case action :: tail =>
action match {
case Check() => label(acc, tail)
case Step(before, action) => label(acc, action :: before :: tail)
case a: SimpleAction[_, _] => label(label(a) :: acc, tail)
}
}
def label[B, A](action: SimpleAction[B, A]): String = action match {
case Start(d) => s"start $d"
case Move(d) => s"move $d"
case Rotate(d) => s"rotate $d"
case Stop(d) => s"stop $d"
case End => "end"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment