Skip to content

Instantly share code, notes, and snippets.

@etorreborre
Forked from tonymorris/TReader.scala
Created October 13, 2012 11:38
Show Gist options
  • Save etorreborre/3884259 to your computer and use it in GitHub Desktop.
Save etorreborre/3884259 to your computer and use it in GitHub Desktop.
Reader monad in Scala
// solution generalizing to any type T
case class TReader[T, +A](run: T => A) {
def map[B](f: A => B): TReader[T, B] =
TReader((t: T) => f(run(t)))
def flatMap[B](f: A => TReader[T, B]): TReader[T, B] =
TReader((t: T) => f(run(t)).run(t))
def &&&[B](x: TReader[T, B]): TReader[T, (A, B)] =
TReader((t: T) => (run(t), x.run(t)))
}
object TReader {
def sequence[T, A](ts: List[TReader[T, A]]): TReader[T, List[A]] =
new TReader((t: T) => ts.map(_.run(t)))
def filter[T, A](p: A => TReader[T, Boolean], as: List[A]): TReader[T, List[A]] =
new TReader((t: T) => as.filter(a => p(a).run(t)))
def fill[T, A](n: Int)(tr: TReader[T, A]): TReader[T, List[A]] =
tr.map((a: A) => List.fill(n)(a))
}
case class T() // your data type that you want to "implicitly" thread through
case class TReader[+A](run: T => A) {
def map[B](f: A => B): TReader[B] =
sys.error("VFJlYWRlcihmIGNvbXBvc2UgcnVuKQ==")
def flatMap[B](f: A => TReader[B]): TReader[B] =
sys.error("VFJlYWRlcih0ID0+IGYocnVuKHQpKSBydW4gdCk=")
def &&&[B](x: TReader[B]): TReader[(A, B)] =
sys.error("ZmxhdE1hcChhID0+IHggbWFwICgoYSwgXykpKQ==")
}
object TReader {
def sequence[A](t: List[TReader[A]]): TReader[List[A]] =
sys.error("dC5mb2xkUmlnaHRbVFJlYWRlcltMaXN0W0FdXV0oVFJlYWRlcihfID0+IE5pbCkpKChhLCBiKSA9PiBmb3IgeyB4IDwtIGE7IHkgPC0gYiB9IHlpZWxkIHg6Onkp")
// Same type as filter, but TReader wraps every type in return position.
// So we "thread through" our T value, while filtering a list.
def filter[A](p: A => TReader[Boolean], a: List[A]): TReader[List[A]] =
sys.error("YS5mb2xkUmlnaHRbVFJlYWRlcltMaXN0W0FdXV0oVFJlYWRlcihfID0+IE5pbCkpKChhLCBiKSA9PiBwKGEpIGZsYXRNYXAgKHIgPT4gaWYocikgYiBtYXAgKGE6Ol8pIGVsc2UgYikp")
def fill[A](n: Int)(t: TReader[A]): TReader[List[A]] =
sys.error("c2VxdWVuY2UoTGlzdC5maWxsKG4pKHQpKQ==")
}
// todo: Use it! Try a for-comprehension to "thread the T through implicitly"
@etorreborre
Copy link
Author

// solution to the previous exercise

case class TReader[T, +A](run: T => A) {
def map[B](f: A => B): TReader[T, B] =
TReader((t: T) => f(run(t)))

def flatMap[B](f: A => TReader[T, B]): TReader[T, B] =
TReader((t: T) => f(run(t)).run(t))

def &&&[B](x: TReader[T, B]): TReader[T, (A, B)] =
TReader((t: T) => (run(t), x.run(t)))
}

object TReader {
def sequence[T, A](ts: List[TReader[T, A]]): TReader[T, List[A]] =
new TReader((t: T) => ts.map(_.run(t)))

def filter[T, A](p: A => TReader[T, Boolean], as: List[A]): TReader[T, List[A]] =
new TReader((t: T) => as.filter(a => p(a).run(t)))

def fill[T, A](n: Int)(tr: TReader[T, A]): TReader[T, List[A]] =
tr.map((a: A) => List.fill(n)(a))
}

@charleso
Copy link

Hey Eric.

I had the same as you. I was wondering about the sequence/filter though, which can be implemented purely in terms of the Monad interface. Tony did exactly that (minus 'point'). Do you know what are the implications are for re-implementing them? Would someone do that for performance, or is the generic implementation always enough?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment