Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- object Website {
- import cats.free.Free
- import cats.Comonad
- import scala.io._
- final case class User(username: String)
- sealed trait Page
- final case object Welcome extends Page
- final case object TryAgain extends Page
- type LoginProgram[A] = Free[LoginOp,A]
- sealed trait LoginOp[A]
- final case class Ask[A](prompt: String, reader: String => A) extends LoginOp[A]
- final case class Retry[A,B](ask: LoginProgram[A], f: A => LoginProgram[Option[B]], tries: Int) extends LoginOp[Option[B]]
- final case class Login(username: String, password: String) extends LoginOp[Option[User]]
- final case class Pure[A](a: A) extends LoginOp[A]
- final case class Display(page: Page) extends LoginOp[Unit]
- object LoginOp {
- def ask[A](prompt: String, reader: String => A) =
- Free.liftF[LoginOp,A](Ask(prompt, reader))
- def retry[A,B](ask: LoginProgram[A], f: A => LoginProgram[Option[B]], tries: Int) =
- Free.liftF[LoginOp,Option[B]](Retry(ask, f, tries))
- def login(username: String, password: String) =
- Free.liftF[LoginOp,Option[User]](Login(username, password))
- def display(page: Page) =
- Free.liftF[LoginOp,Unit](Display(page))
- implicit object loginOpInstances extends Comonad[LoginOp] {
- override def coflatMap[A, B](fa: LoginOp[A])(f: (LoginOp[A]) ⇒ B): LoginOp[B] =
- Pure(f(fa))
- override def extract[A](x: LoginOp[A]): A =
- x match {
- case Login(u, p) => (u, p) match {
- case ("Noel", "password") => Some(User("noelw"))
- case _ => None
- }
- case Display(p) =>
- p match {
- case TryAgain =>
- println("Sorry, couldn't login you in. Try again!")
- case Welcome =>
- println("Welcome back!")
- }
- ()
- case Ask(p, r) =>
- println(p)
- r(StdIn.readLine)
- case r: Retry[a,b] =>
- def loop(counter: Int): Option[b] =
- counter match {
- case 0 => None
- case n => r.f(r.ask.run: a).run match {
- case None => loop(n - 1)
- case Some(a) => Some(a)
- }
- }
- loop(r.tries)
- case Pure(a) => a
- }
- override def map[A, B](fa: LoginOp[A])(f: (A) ⇒ B): LoginOp[B] =
- Pure(f(extract(fa)))
- }
- }
- val loginPrompt = LoginOp.ask("Enter login stuff", (s: String) => {
- val split = s.split(" ")
- (split(0), split(1))
- })
- def loginAttempt(details: (String, String)) = {
- val (u, p) = details
- LoginOp.login(u, p)
- }
- val attemptLogin = LoginOp.retry(loginPrompt, loginAttempt, 3)
- object Example {
- val login =
- for {
- user <- attemptLogin
- page <- LoginOp.display(user.fold[Page](TryAgain){ u => Welcome })
- } yield page
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement