Guest User

Untitled

a guest
Aug 7th, 2018
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.12 KB | None | 0 0
  1. package io.github.themirrortruth
  2.  
  3. object KVStore {
  4. def main(args: Array[String]): Unit = {
  5. import cats.free.Free
  6. import cats.{Id, ~>}
  7.  
  8. //Strict language for working with abstract key-value stores defined as Algebraic Data Type (ADT)
  9. sealed trait KVStoreApi[Key, Value, Result]
  10. extends Product
  11. with Serializable
  12. //Encodes 'get by key' operation into the case class
  13. final case class Get[Key, Value](key: Key)
  14. extends KVStoreApi[Key, Value, Option[Value]]
  15.  
  16. //Domain model
  17. final case class User(username: String, password: String)
  18.  
  19. //Second, higher level language than 'KVStoreApi' that can be compiled into it
  20. sealed trait UserApi[Result] extends Product with Serializable
  21. //Encodes 'get user by username and password' into the case class
  22. final case class GetUser(username: String, password: String)
  23. extends UserApi[Option[User]]
  24.  
  25. //lifts the 'Get by key' operation into Free Monad context, so we will be able to use 'for comprehension' syntax
  26. def get[Key, Value](
  27. key: Key): Free[KVStoreApi[Key, Value, ?], Option[Value]] =
  28. Free.liftF(Get[Key, Value](key): KVStoreApi[Key, Value, Option[Value]])
  29. //lifts the 'Get user by username and password' operation into Free Monad context, so we will be able to use 'for comprehension' syntax
  30. def getUser(userName: String,
  31. password: String): Free[UserApi, Option[User]] =
  32. Free.liftF(GetUser(userName, password))
  33.  
  34. //UserApi to KVStoreApi interpreter, interprets operations from UserAPI ADT into the lower-level KvStoreAPI
  35. val userApiToKvInterpreter =
  36. new (UserApi ~> Free[KVStoreApi[String, String, ?], ?]) {
  37. override def apply[A](
  38. fa: UserApi[A]): Free[KVStoreApi[String, String, ?], A] = fa match {
  39. case GetUser(username, password) =>
  40. get[String, String](username).map {
  41. case Some(p) if p == password => Some(User(username, p))
  42. case _ => None
  43. }
  44. }
  45. }
  46.  
  47. //Interprets operations from KVStoreAPI into the most lower-level Monad one.
  48. //Here is 'Id' monad is used, but it can be any Monad like 'Task' from Monix, 'IO' from cats-effect, etc.
  49. val userKvToIdInterpreter = new (KVStoreApi[String, String, ?] ~> Id) {
  50. private val usernamesToPasswords = Map("some_user" -> "some_password")
  51.  
  52. override def apply[A](fa: KVStoreApi[String, String, A]): Id[A] = {
  53. fa match {
  54. case Get(key: String) => usernamesToPasswords.get(key)
  55. }
  56. }
  57. }
  58.  
  59.  
  60. println(
  61. //the operation is not running yet here, we just have built a description of program that can introspected later
  62. getUser("some_user", "some_password")
  63. //compiling description of the program into the KVStoreAPI operations
  64. .foldMap(userApiToKvInterpreter)
  65. //compiling operations from KVStoreAPI into the 'Id' monad, 'Id' monad is runnning eagerly, so it will print a result.
  66. //however, if we use something like Monix' Task, then we'd have to run it on some 'Scheduler' (analogue of ExecutionContext for Futures)
  67. .foldMap(userKvToIdInterpreter))
  68. }
  69. }
Add Comment
Please, Sign In to add comment