Skip to content

Instantly share code, notes, and snippets.

@n4to4
Created November 8, 2017 02:40
Show Gist options
  • Select an option

  • Save n4to4/d63e6a7880e95da733d9d9e394d51928 to your computer and use it in GitHub Desktop.

Select an option

Save n4to4/d63e6a7880e95da733d9d9e394d51928 to your computer and use it in GitHub Desktop.
object Main extends App {
sealed trait VState
final abstract class Idle extends VState
final abstract class Half extends VState
final abstract class Ready extends VState
type Drink = String
sealed trait Coin
case object FiftyCents extends Coin
case object OneEuro extends Coin
final class VendingMachine[S <: VState] private {
def insertFirst50()(implicit ev: S =:= Idle): VendingMachine[Half] =
new VendingMachine
def insertSecond50()(implicit ev: S =:= Half): VendingMachine[Ready] =
new VendingMachine
def insertEuro()(implicit ev: S =:= Idle): VendingMachine[Ready] =
new VendingMachine
def pushButton()(implicit ev: S =:= Ready): (VendingMachine[Idle], Drink) =
(new VendingMachine[Idle], "Fizz")
def abort[T <: VState, O <: Coin]()(implicit ev: Next.Aux[S, T, O]): (VendingMachine[T], O) =
(new VendingMachine[T], ev.coin)
}
object VendingMachine {
def initial: VendingMachine[Idle] =
new VendingMachine
}
sealed abstract class Next[S <: VState] {
type Next <: VState
type Out <: Coin
def coin: Out
}
final object Next {
type Aux[S0 <: VState, N0 <: VState, O0 <: Coin] =
Next[S0] {
type Next = N0
type Out = O0
}
}
implicit val halfAbort: Next.Aux[Half, Idle, FiftyCents.type] = new Next[Half] {
type Next = Idle
type Out = FiftyCents.type
override val coin = FiftyCents
}
implicit val readyAbort: Next.Aux[Ready, Idle, OneEuro.type] = new Next[Ready] {
type Next = Idle
type Out = OneEuro.type
override val coin = OneEuro
}
def run(): Unit = {
val machine = VendingMachine.initial
// machine.insertSecond50() // Cannot prove that Main.Idle =:= Main.Half.
// machine.abort() // could not find implicit value for parameter ev: Main.Next.Aux[Main.Idle,T,O]
machine.insertFirst50().insertSecond50().pushButton()
machine.insertEuro().pushButton()
val coin1 = machine.insertFirst50().abort()._2
assert(coin1 == FiftyCents)
// machine.insertSecond50() // Cannot prove that Main.Idle =:= Main.Half
val coin2 = machine.insertEuro().abort()._2
assert(coin2 == OneEuro)
}
run
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment