Skip to content

Instantly share code, notes, and snippets.

@Mortimerp9
Created April 16, 2013 23:20
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Mortimerp9/5400482 to your computer and use it in GitHub Desktop.
a monad transformer for Reader presented in this gist: https://gist.github.com/Mortimerp9/5400194 This is a companion code for the following post: https://coderwall.com/p/ibrhta
import scala.concurrent.Future
import scala.concurrent.ExecutionContext
/**
* a ReaderM is a tool for Readers containing other monads, such as Reader[_, Option[_]] or Reader[_, Future[_]]
*/
case class ReaderM[-C, A, M[+A]](val read: Reader[C, M[A]])(implicit canMap: CanMap[A, _, M]) {
def apply(conn: C): M[A] = read(conn)
/**
* transform the value within the embedded monad
*/
def map[B](f: A => B)(implicit canMap: CanMap[A, B, M]): Reader[C, M[B]] = read.map(in => canMap.map(in)(f))
/**
* transform the value within the embedded monad, in the case where we get a ReaderM back
*/
def flatMap[B, D <: C](f: A => ReaderM[D, B, M])(implicit canMap: CanMap[A, B, M], canMapB: CanMap[B, _, M]): ReaderM[D, B, M] = ReaderM[D, B, M](read.flatMap { in =>
Reader.reader { (conn: D) =>
canMap.flatMap(in) { a: A =>
f(a)(conn)
}
}
})
/**
* transform the value within the embedded monad, in the case where we get a Reader with a pure value inside
*/
def flatMapMap[B, D <: C](f: A => Reader[D, B])(implicit canMap: CanMap[A, B, M], canMapB: CanMap[B, _, M]): ReaderM[D, B, M] = ReaderM[D, B, M](read.flatMap { in =>
Reader.reader { (conn: D) =>
canMap.map(in) { a: A =>
f(a)(conn)
}
}
})
/**
* Combine two readers
*/
def zip[B, D <: C](other: ReaderM[D, B, M])(implicit canMapAB: CanMap[(A, B), _, M], canMap: CanMap[A, (A, B), M], canMapB: CanMap[B, (A, B), M]): ReaderM[D, (A, B), M] = ReaderM.readerM { conn: D =>
val a = apply(conn)
val b = other(conn)
canMap.flatMap(a)(av => canMapB.map(b)((av, _)))
}
}
object ReaderM {
/**
* transforms a Future[Reader[A, B]] in a Reader[A, Future[B]]
*/
implicit def moveFuture[A, B](future: Future[Reader[A, B]])(implicit context: ExecutionContext): Reader[A, Future[B]] =
Reader.sequence(future)
implicit def moveFutureFuture[A, B](future: Future[Reader[A, Future[B]]])(implicit context: ExecutionContext): Reader[A, Future[B]] = {
val future1 = moveFuture(future)
future1.map(f => f.flatMap(inf => inf))
}
def pure[C, B, M[+_]](value: B)(implicit canMap: CanMap[B, B, M]): ReaderM[C, B, M] =
ReaderM[C, B, M](Reader.pure[C, M[B]](canMap.pure(value)))
/**
* implicit conversion back to a Reader.
*/
implicit def toReader[C, M[_]](rm: ReaderM[C, _, M]): Reader[C, M[_]] = rm.read;
implicit def readerM[C, A, M[+A]](r: Reader[C, M[A]])(implicit canMap: CanMap[A, _, M]): ReaderM[C, A, M] = ReaderM[C, A, M](r)
/**
* provide a connection to read the content of a reader
*/
def withConnection[C, R, M[+R]](connection: C)(r: ReaderM[C, R, M]): M[R] = r(connection)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment