Skip to content

Instantly share code, notes, and snippets.

@juanjovazquez
Last active July 5, 2017 21:57
Show Gist options
  • Save juanjovazquez/31cc992e42a91c08c6c01fb3b8e6a2b0 to your computer and use it in GitHub Desktop.
Save juanjovazquez/31cc992e42a91c08c6c01fb3b8e6a2b0 to your computer and use it in GitHub Desktop.
import cats.~>
import cats.data.State
import cats.syntax.all._
import org.atnos.eff.{ |=, <=, Eff, Fx, Member }
import org.atnos.eff.state.get
import org.atnos.eff.either.fromEither
import org.atnos.eff.Eff.send
import org.atnos.eff.Interpret.translateNat
import org.atnos.eff.syntax.all._
object EffPoC {
// model
case class Foo(id: Long)
case class Bar(id: Long)
case class MyError(msg: String)
// custom effects
sealed abstract class FooRepoEffect[A]
object FooRepoEffect {
type _fooRepo[R] = FooRepoEffect |= R
type _FooRepo[R] = FooRepoEffect <= R
case class FindById(id: Long) extends FooRepoEffect[Foo]
object syntax {
def findById[R: _fooRepo](id: Long): Eff[R, Foo] =
send(FindById(id))
}
object interpreters {
import RepoImpls._
def fromFooEffectToDBAction[R: _DBAction: _Error]: FooRepoEffect ~> Eff[R, ?] =
Lambda[FooRepoEffect ~> Eff[R, ?]] {
case FindById(id) => FooRepo.findById(id)
}
def runFooRepo[R, T, U](p: Eff[R, T])(
implicit M: Member.Aux[FooRepoEffect, R, U],
DB: _DBAction[U],
E: _Error[U]
): Eff[U, T] = translateNat(p)(fromFooEffectToDBAction)
}
implicit class FooRepoEffectOps[R, T, U](p: Eff[R, T]) {
import RepoImpls._
def runFooRepo(
implicit M: Member.Aux[FooRepoEffect, R, U],
DB: _DBAction[U],
E: _Error[U]
): Eff[U, T] = interpreters.runFooRepo(p)
}
}
sealed abstract class BarRepoEffect[A]
object BarRepoEffect {
type _barRepo[R] = BarRepoEffect |= R
type _BarRepo[R] = BarRepoEffect <= R
case class FindById(id: Long) extends BarRepoEffect[Bar]
object syntax {
def findById[R: _barRepo](id: Long): Eff[R, Bar] =
send(FindById(id))
}
object interpreters {
import RepoImpls._
def fromBarEffectToDBAction[R: _DBAction: _Error]: BarRepoEffect ~> Eff[R, ?] =
Lambda[BarRepoEffect ~> Eff[R, ?]] {
case FindById(id) => BarRepo.findById(id)
}
def runBarRepo[R, T, U](p: Eff[R, T])(
implicit M: Member.Aux[BarRepoEffect, R, U],
DB: _DBAction[U],
E: _Error[U]
): Eff[U, T] = translateNat(p)(fromBarEffectToDBAction)
}
implicit class BarRepoEffectOps[R, T, U](p: Eff[R, T]) {
import RepoImpls._
def runBarRepo(
implicit M: Member.Aux[BarRepoEffect, R, U],
DB: _DBAction[U],
E: _Error[U]
): Eff[U, T] = interpreters.runBarRepo(p)
}
}
object RepoImpls {
type Foos = Map[Long, Foo]
type Bars = Map[Long, Bar]
case class DB(foos: Foos, bars: Bars)
val initialDB = DB(Map.empty[Long, Foo], Map.empty[Long, Bar])
type DBAction[A] = State[DB, A]
type _dbAction[R] = DBAction |= R
type _DBAction[R] = DBAction <= R
type Error[A] = MyError Either A
type _error[R] = Error |= R
type _Error[R] = Error <= R
object FooRepo {
def findById[R: _dbAction: _error](id: Long): Eff[R, Foo] =
for {
foo <- get.map(_.foos.get(id))
r <- fromEither(foo.fold(MyError("Not Found").asLeft[Foo])(_.asRight))
} yield r
}
object BarRepo {
def findById[R: _dbAction: _error](id: Long): Eff[R, Bar] =
for {
bar <- get.map(_.bars.get(id))
r <- fromEither(bar.fold(MyError("Not Found").asLeft[Bar])(_.asRight))
} yield r
}
}
import RepoImpls._
type Stack = Fx.fx4[FooRepoEffect, BarRepoEffect, DBAction, Error]
def runAll[A](p: Eff[Stack, A]): (Error[A], DB) =
p.runFooRepo.runBarRepo.runEither.runState(initialDB).run
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment