Skip to content

Instantly share code, notes, and snippets.

@SegFaultAX
Last active March 22, 2019 21:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SegFaultAX/1d3b93090ebfd653f3d6ba74c594f988 to your computer and use it in GitHub Desktop.
Save SegFaultAX/1d3b93090ebfd653f3d6ba74c594f988 to your computer and use it in GitHub Desktop.
Reader Monad [Scala]
object ReaderMonad extends App {
case class Reader[-E, +A](run: E => A) {
def map[B](fn: A => B): Reader[E, B] =
dimap((e: E) => e)(fn)
def contramap[E0](fn: E0 => E): Reader[E0, A] =
dimap(fn)(a => a)
def dimap[E0, B](f: E0 => E)(g: A => B): Reader[E0, B] =
Reader(e0 => g(run(f(e0))))
def map2[EE <: E, B, C](r: Reader[EE, B])(fn: (A, B) => C): Reader[EE, C] =
Reader(e => fn(run(e), r.run(e)))
def flatMap[EE <: E, B](fn: A => Reader[EE, B]): Reader[EE, B] =
Reader(e => fn(run(e)).run(e))
}
object Reader {
def unit[E, A](a: A): Reader[E, A] =
Reader(_ => a)
def ask[E]: Reader[E, E] =
Reader(e => e)
def asks[E, A](fn: E => A): Reader[E, A] =
ask.map(fn)
def local[E, A](r: Reader[E, A])(fn: E => E): Reader[E, A] =
Reader(e => r.run(fn(e)))
def traverse[E, A, B](as: List[A])(fn: A => Reader[E, B]): Reader[E, List[B]] =
as.foldRight[Reader[E, List[B]]](unit(List()))((e, a) => fn(e).map2(a)(_ :: _))
def sequence[E, A](rs: List[Reader[E, A]]): Reader[E, List[A]] =
traverse(rs)(a => a)
}
/*
import Reader._
case class Person(name: String, age: Int)
val result: Reader[Person, String] = for {
name <- asks(_.name) // not able to infer `E = Person`
age <- asks(_.age) // not able to infer `E = Person`
} yield "$name is $age years old"
println(result.run(Person("Test", 31)))
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment