Created
November 28, 2016 21:22
-
-
Save lamdor/1798d7e43abfd59602129f76b89cc4b0 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
package eg.repository.interpreter | |
import cats.{ MonadError, MonadState } | |
import java.time.LocalDate | |
import scala.collection.mutable.{Map => MMap} | |
import cats.Eval | |
import cats.arrow.FunctionK | |
import cats.data.StateT | |
import cats.instances.either._ | |
import eg.model._ | |
import eg.repository.AccountRepository._ | |
trait AccountRepoInterpreter[M[_]] { | |
def apply[A](action: AccountRepo[A]): M[A] | |
} | |
case class AccountRepoMutableInterpreter() extends AccountRepoInterpreter[Eval] { | |
val table = MMap.empty[String, Account] | |
val step: FunctionK[AccountRepoF, Eval] = new FunctionK[AccountRepoF, Eval] { | |
override def apply[A](fa: AccountRepoF[A]): Eval[A] = fa match { | |
case Query(no) => | |
table.get(no) | |
.map(a => Eval.now(a)) | |
.getOrElse { Eval.later(sys.error("Not found")) } | |
case Store(account) => | |
Eval.now(table += ((account.no, account))).map(_ => ()) | |
case Delete(no) => | |
Eval.now(table -= no).map(_ => ()) | |
} | |
} | |
def apply[A](action: AccountRepo[A]): Eval[A] = | |
action.foldMap(step) | |
} | |
object AccountRepoState { | |
type AccountMap = Map[String, Account] | |
type Err[A] = Either[String, A] | |
type AccountState[A] = StateT[Err, AccountMap, A] | |
} | |
object AccountRepoStateInterpreter extends AccountRepoInterpreter[AccountRepoState.AccountState] { | |
import AccountRepoState._ | |
import StateT._ | |
def interpret[A](fa: AccountRepoF[A]): AccountState[A] = fa match { | |
case Query(no) => | |
inspectF { table => // could use getAccount from below | |
table.get(no) match { | |
case Some(a) => Right(a) | |
case None => Left("not found") | |
} | |
} | |
case Store(account) => modify(_.updated(account.no, account)) | |
case Delete(no) => modify(_ - no) | |
} //.asInstanceOf[AccountState[A]] // needed when it was AccountRepoF[+A], variance made things go sideways | |
def apply[B](action: AccountRepo[B]): AccountState[B] = | |
action.foldMap(FunctionK.lift(interpret)) | |
private[this] def getAccount(no: String)(table: AccountMap): Err[Account] = { | |
table.get(no) match { | |
case Some(a) => Right(a) | |
case None => Left("not found") | |
} | |
} | |
} | |
case class AccountRepoMonadClassInterpreter[M[_]]( | |
implicit monadState: MonadState[M, AccountRepoState.AccountMap], | |
monadError: MonadError[M, String] | |
) extends AccountRepoInterpreter[M] { | |
def interpret[A](fa: AccountRepoF[A]): M[A] = fa match { | |
case Query(no) => | |
monadState.flatMap(monadState.get) { table => | |
table.get(no) match { | |
case None => monadError.raiseError[Account]("not found") | |
case Some(a) => monadState.pure(a) | |
} | |
} | |
case Store(account) => monadState.modify(_.updated(account.no, account)) | |
case Delete(no) => monadState.modify(_ - no) | |
} | |
def apply[B](action: AccountRepo[B]): M[B] = | |
action.foldMap(FunctionK.lift(interpret))(monadState) | |
} | |
object TestInterpreters { | |
def main(args: Array[String]): Unit = { | |
val instructions = for { | |
_ <- store(Account("1234", "Checking", LocalDate.now)) | |
// a <- query("1234") | |
_ <- update("1234", a => a.copy(name = "Checking 2")) | |
_ <- query("1234") | |
// _ <- delete("1234") | |
a2 <- query("1234") | |
} yield a2 | |
val compiledViaState = AccountRepoStateInterpreter.apply(instructions) | |
val resultViaState = compiledViaState.run(Map.empty) | |
println(s"resultViaState = ${resultViaState}") | |
val compiledViaStateViaClasses = | |
AccountRepoMonadClassInterpreter.apply[AccountRepoState.AccountState].apply(instructions) | |
val resultViaStateViaClasses = compiledViaStateViaClasses.run(Map.empty) | |
println(s"resultViaStateViaClasses = ${resultViaStateViaClasses}") | |
val mutableInterpreter = AccountRepoMutableInterpreter() | |
val compiledViaMutable = mutableInterpreter.apply(instructions) | |
val resultViaMutable = compiledViaMutable.value | |
println(s"resultViaMutable = ${resultViaMutable}") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment