-
-
Save etorreborre/3884259 to your computer and use it in GitHub Desktop.
Reader monad in Scala
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
// 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)) | |
} |
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
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" |
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
// 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))
}