Created
October 16, 2013 03:29
-
-
Save anonymous/7002273 to your computer and use it in GitHub Desktop.
Functional coin toss (using hastily constructed partial abstractions)
This file contains 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
sealed trait Command | |
case object Bid extends Command | |
case object Quit extends Command | |
case object Unrecognized extends Command | |
sealed trait Result | |
case object Won extends Result | |
case object Lost extends Result | |
sealed trait Face | |
case object Heads extends Face | |
case object Tails extends Face | |
case class Bet(amount: Int, on: Face) | |
case class Player(name: String, money: Int = 100) | |
object Program extends IOProgram { | |
def IOMain(): IO[Unit] = { | |
for ( | |
_ <- IO { print("Your name? ") }; | |
a <- IO { readLine }; | |
_ <- IO { println("Hello, " + a) }; | |
_ <- gameLoop(Player(name = a), rng(1, 2)) | |
) yield { | |
} | |
} | |
def gameLoop(p: Player, rngs: Stream[Int]): IO[Unit] = { | |
for ( | |
_ <- IO { println("Hey " + p.name + ", looks like you want to make a bet with that juicy $" + p.money + " you're waving around!") }; | |
_ <- IO { print("Would you like to (B)id or (Q)uit? ") }; | |
c <- IO { parseCommand(readLine) }; | |
_ <- executeCommand(c, p, rngs) | |
) yield { | |
} | |
} | |
def executeCommand(c: Command, p: Player, rngs: Stream[Int]): IO[Unit] = c match { | |
case Bid => for ( | |
b <- bidLoop; | |
_ <- announceResult(b, bet(b, rngs)); | |
_ <- handleResult(b, bet(b, rngs), p, rngs) | |
) yield { | |
} | |
case Quit => IO.Unit | |
case Unrecognized => for ( | |
_ <- IO { println("I didn't recognize that command.") }; | |
_ <- gameLoop(p, rngs) | |
) yield {} | |
} | |
def handleResult(b: Bet, r: Result, p: Player, rngs: Stream[Int]): IO[Unit] = r match { | |
case Won => gameLoop(p.copy(money = p.money + b.amount), rngs.tail) | |
case Lost => gameLoop(p.copy(money = p.money - b.amount), rngs.tail) | |
} | |
def parseCommand(s: String): Command = s.toLowerCase.head match { | |
case 'b' => Bid | |
case 'q' => Quit | |
case _ => Unrecognized | |
} | |
def announceResult(b: Bet, r: Result): IO[Unit] = r match { | |
case Won => IO { println("You won $" + b.amount + "! Congratulations!\n") } | |
case Lost => IO { println("You lost $" + b.amount + "! Sucker!!!!\n") } | |
} | |
def bidLoop(): IO[Bet] = { | |
for ( | |
a <- amountLoop; | |
f <- faceLoop | |
) yield { | |
Bet(a, f) | |
} | |
} | |
def amountLoop(): IO[Int] = { | |
for ( | |
_ <- IO { print("Amount to bet? ") }; | |
a <- IO { readLine } | |
) yield { | |
safeParseInt(a).getOrElse((IO { println("Invalid amount there buddy. Try again!") }).flatMap(x => amountLoop).unsafePerformIO) | |
} | |
} | |
def faceLoop(): IO[Face] = { | |
for ( | |
_ <- IO { print("(H)eads or (T)ails? ") }; | |
f <- IO { readLine } | |
) yield { | |
safeStringToFace(f).getOrElse((IO { println("Make a real call!") }).flatMap(x => faceLoop).unsafePerformIO) | |
} | |
} | |
def safeStringToFace(s: String): Option[Face] = s.toLowerCase.head match { | |
case 'h' => Some(Heads) | |
case 't' => Some(Tails) | |
case _ => None | |
} | |
def safeParseInt(s: String): Option[Int] = try { | |
Some(s.toInt) | |
} catch { | |
case _ => None | |
} | |
val r = new java.util.Random | |
def rng(min: Int, max: Int): Stream[Int] = Stream.cons(r.nextInt((max - min) + 1) + min, rng(min, max)) | |
def toss(rngs: Stream[Int]): Face = if (rngs.head % 2 == 0) Heads else Tails | |
def bet(b: Bet, rngs: Stream[Int]): Result = if (b.on == toss(rngs)) Won else Lost | |
} |
This file contains 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
import scala.{Unit => U} | |
object IO { | |
def apply[A](f: => A): IO[A] = new IOMonad(U => f) | |
val Unit = apply(()) | |
} | |
sealed trait IO[A] { | |
def unsafePerformIO: A | |
def map[B](f: A => B): IO[B] | |
def flatMap[B](f: A => IO[B]): IO[B] | |
} | |
private class IOMonad[A](action: U => A) extends IO[A] { | |
def unsafePerformIO: A = action() | |
def map[B](f: A => B): IO[B] = new IOMonad(f.compose(action)) | |
def flatMap[B](f: A => IO[B]): IO[B] = new IOMonad(U => f.compose(action)().unsafePerformIO) | |
} | |
abstract class IOProgram { | |
def IOMain(): IO[U] | |
def main(args: Array[String]): U = IOMain.unsafePerformIO | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment