Created
November 8, 2017 02:40
-
-
Save n4to4/d63e6a7880e95da733d9d9e394d51928 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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