Last active
May 29, 2016 10:43
-
-
Save linqing/0dbd9e88fbf982a1a84eeb3b0fbbd8a3 to your computer and use it in GitHub Desktop.
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
package demo.app | |
// 从雪山大虫scalaz example改写为使用cats | |
import cats.Functor | |
import cats.data.{Coproduct, Reader, Xor} | |
import cats.free.{Free, Inject} | |
import cats._ | |
import cats.std.all._ | |
import cats.syntax.all._ | |
import scala.io.StdIn | |
import scala.language.{higherKinds, implicitConversions} | |
object FreeADTs { | |
sealed trait Interact[+A] | |
case class Ask[A](prompt: String, onInput: String => A) extends Interact[A] | |
case class Tell[A](msg: String, next: A) extends Interact[A] | |
sealed trait InteractInstances { | |
object InteractFunctor extends Functor[Interact] { | |
def map[A, B](ia: Interact[A])(f: A => B): Interact[B] = ia match { | |
case Ask(prompt, input) => Ask(prompt, input andThen f) | |
case Tell(msg, next) => Tell(msg, f(next)) | |
} | |
} | |
} | |
sealed trait InteractFunctions { | |
def ask[G[_], A](p: String, f: String => A)(implicit I: Inject[Interact, G]): Free[G, A] = | |
Free.liftF(I.inj(Ask(p, f))) | |
def tell[G[_]](m: String)(implicit I: Inject[Interact, G]): Free[G, Unit] = | |
Free.liftF(I.inj(Tell(m, Free.pure(())))) | |
} | |
object Interacts extends InteractInstances with InteractFunctions | |
sealed trait UserLogin[+A] | |
// None Functor 高阶类 | |
case class CheckId(uid: String) extends UserLogin[Boolean] | |
case class Login(uid: String, pswd: String) extends UserLogin[Boolean] | |
sealed trait LoginFunctions { | |
def checkId[G[_]](uid: String)(implicit I: Inject[UserLogin, G]): Free[G, Boolean] = | |
Free.liftF(I.inj(CheckId(uid))) | |
def login[G[_]](uid: String, pswd: String)(implicit I: Inject[UserLogin, G]): Free[G, Boolean] = | |
Free.liftF(I.inj(Login(uid, pswd))) | |
} | |
object Logins extends LoginFunctions | |
sealed trait Permission[+A] | |
case class HasPermission(uid: String, acc: Int) extends Permission[Boolean] | |
sealed trait PermissionFunctions { | |
def hasPermission[G[_]](uid: String, acc: Int)(implicit I: Inject[Permission, G]): Free[G, Boolean] = | |
Free.liftF(I.inj(HasPermission(uid, acc))) | |
} | |
object Permissions extends PermissionFunctions | |
} | |
object FreeASTs { | |
import FreeADTs._ | |
import Interacts._ | |
val interactScript = for { | |
first <- ask("what's your first name?", identity) | |
last <- ask("your last name?", _.toUpperCase()) | |
_ <- tell(s"hello, $first $last") | |
} yield () | |
import Logins._ | |
type InteractLogin[A] = Coproduct[Interact, UserLogin, A] | |
val loginScript = for { | |
uid <- ask[InteractLogin, String]("what's you id?", identity) | |
idok <- checkId[InteractLogin](uid) | |
_ <- if (idok) tell[InteractLogin](s"hi, $uid") else tell[InteractLogin]("sorry, don't know you!") | |
pwd <- if (idok) ask[InteractLogin, String](s"what's your password?", identity) | |
else Free.pure[InteractLogin, String]("") | |
login <- if (idok) login[InteractLogin](uid, pwd) | |
else Free.pure[InteractLogin, Boolean](false) | |
_ <- if (login) tell[InteractLogin](s"congratulations,$uid") | |
else tell[InteractLogin](if (idok) "sorry, no pass!" else "") | |
} yield login | |
import Permissions._ | |
type InteractLoginPermission[A] = Coproduct[Permission, InteractLogin, A] | |
type T[A] = InteractLoginPermission[A] | |
val authScript = for { | |
uid <- ask[T, String]("what's you id?", identity) | |
idok <- checkId[T](uid) | |
_ <- if (idok) tell[T](s"hi, $uid") | |
else tell[T]("sorry, don't know you!") | |
pwd <- if (idok) ask[T, String](s"what's your password?", identity) | |
else Free.pure[T, String]("") | |
login <- if (idok) login[T](uid, pwd) | |
else Free.pure[T, Boolean](false) | |
_ <- if (login) tell[T](s"congratulations,$uid") | |
else tell[T](if (idok) "sorry, no pass!" else "") | |
acc <- if (login) ask[T, Int](s"what level of access, $uid?", _.toInt) | |
else Free.pure[T, Int](0) | |
perm <- if (login) hasPermission[T](uid, acc) | |
else Free.pure[T, Boolean](false) | |
_ <- if (perm) tell[T](s"you may use the system,$uid") | |
else tell[T](if (idok && login) "sorry, it's above your pay grade!" else "") | |
} yield () | |
} | |
object FreeInterps { | |
import FreeADTs._ | |
object InteractConsole extends (Interact ~> Id) { | |
def apply[A](ia: Interact[A]): Id[A] = ia match { | |
case Ask(p, onInput) => println(p); onInput(StdIn.readLine) | |
case Tell(m, n) => println(m); n | |
} | |
} | |
import Dependencies._ | |
type AuthReader[A] = Reader[Authenticator, A] | |
object InteractLogin extends (Interact ~> AuthReader) { | |
def apply[A](ia: Interact[A]): AuthReader[A] = ia match { | |
case Ask(p, onInput) => println(p); Reader { m => onInput(StdIn.readLine) } | |
case Tell(msg, n) => println(msg); Reader { m => n } | |
} | |
} | |
object LoginConsole extends (UserLogin ~> AuthReader) { | |
def apply[A](ua: UserLogin[A]): AuthReader[A] = ua match { | |
case CheckId(uid) => Reader { m => m.validateId(uid) } | |
case Login(uid, pwd) => Reader { m => m.validatePassword(uid, pwd) } | |
} | |
} | |
object PermConsole extends (Permission ~> AuthReader) { | |
def apply[A](pa: Permission[A]): AuthReader[A] = pa match { | |
case HasPermission(uid, acc) => Reader { m => m.grandAccess(uid, acc) } | |
} | |
} | |
def or[F[_], H[_], G[_]](f: F ~> G, h: H ~> G) = | |
new (({type l[x] = Coproduct[F, H, x]})#l ~> G) { | |
def apply[A](ca: Coproduct[F, H, A]): G[A] = ca.run match { | |
case Xor.Left(fg) => f(fg) | |
case Xor.Right(hg) => h(hg) | |
} | |
} | |
def among3[F[_], H[_], K[_], G[_]](f: F ~> G, h: H ~> G, k: K ~> G) = { | |
type FH[A] = Coproduct[F, H, A] | |
type KFH[A] = Coproduct[K, FH, A] | |
new (({type l[x] = Coproduct[K, FH, x]})#l ~> G) { | |
def apply[A](kfh: KFH[A]): G[A] = kfh.run match { | |
case Xor.Left(kg) => k(kg) | |
case Xor.Right(cfh) => cfh.run match { | |
case Xor.Left(fg) => f(fg) | |
case Xor.Right(hg) => h(hg) | |
} | |
} | |
} | |
} | |
} | |
object Dependencies { | |
trait UserControl { | |
val pswdMap: Map[String, String] | |
def validateId(uid: String): Boolean | |
def validatePassword(uid: String, pswd: String): Boolean | |
} | |
trait AccessControl { | |
val accMap: Map[String, Int] | |
def grandAccess(uid: String, acc: Int): Boolean | |
} | |
trait Authenticator extends UserControl with AccessControl | |
} | |
object FreeDemo extends App { | |
import Dependencies._ | |
import FreeASTs._ | |
import FreeInterps._ | |
object AuthControl extends Authenticator { | |
val pswdMap = Map( | |
"Tiger" -> "1234", | |
"John" -> "0000" | |
) | |
override def validateId(uid: String) = | |
pswdMap.getOrElse(uid, "???") =!= "???" | |
override def validatePassword(uid: String, pswd: String) = | |
pswdMap.getOrElse(uid, pswd + "!") === pswd | |
val accMap = Map( | |
"Tiger" -> 8, | |
"John" -> 0 | |
) | |
override def grandAccess(uid: String, acc: Int) = | |
accMap.getOrElse(uid, -1) > acc | |
} | |
authScript.foldMap(among3(InteractLogin, LoginConsole, PermConsole)).run(AuthControl) | |
// loginScript.foldMapRec(or(InteractLogin,LoginConsole)).run(AuthControl) | |
// interactScript.foldMap(InteractConsole) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment