Reputation: 1059
To learn how to create monads and to learn their internal mechanisms I am trying to implement a version of Writer following this awsome article: You Could Have Invented Monads!
The problem I'm facing is that I can't use the data structures I created in a for comprehension.
This is a cleaned extract of my code:
case class Writer( x: Double, log: String = " Beginning of log") {
def flatMap( f: Double => Writer ): Writer = f( x ) match {
case Writer( fx, msg ) => Writer( fx, log + "\n " + msg )
}
def map( f: Double => Double ): Double = f( x )
}
def outer( x: Double ) = Writer( Math.log( x ), s"Called outer($x)" )
def inner( x: Double ) = Writer( Math.exp( x ), s"Called inner($x)" )
def example( x: Double ): Unit = {
// This works
val result = Writer( x ).flatMap {
y1 => inner(y1).flatMap { y2 => outer(y2) }
}
println( result )
// This works
val result2 = for { y1 <- Writer(x) } yield {
for { y2 <- inner( y1 ) } yield {
for { y3 <- outer( y2 ) } yield y3
}
}
println( result2 )
// THIS DOESN'T WORK
val result3 = for {
y1 <- Writer(x)
y2 <- inner( y1 )
y3 <- outer( y2 )
} yield y3 // Or whatever, it doesn't work
println( result3 )
}
My full code is available here: Debuggable.scala
What I cannot figure out is what's missing in my code. It's like the scalaz implementation returns something that is good in a for comprehension and then you run it with a specific method. But can't find how to do it myself
Upvotes: 0
Views: 836
Reputation: 44918
Here is how you can force it to compile:
case class Writer(x: Double, log: String = " Beginning of log") {
def flatMap(f: Double => Writer): Writer = f(x) match {
case Writer(fx, msg) => Writer(fx, log + "\n " + msg)
}
def map(f: Double => Double): Writer = Writer(f(x), log)
}
def outer(x: Double) = Writer(Math.log(x), s"Called outer($x)")
def inner(x: Double) = Writer(Math.exp(x), s"Called inner($x)")
def example(x: Double): Unit = {
val result = Writer(x).flatMap {
y1 => inner(y1).flatMap { y2 => outer(y2) }
}
println(result)
val result3 = for {
y1 <- Writer(x)
y2 <- inner(y1)
y3 <- outer(y2)
} yield y3
println(result3)
}
example(42)
prints:
Writer(42.0, Beginning of log
Called inner(42.0)
Called outer(1.73927494152050099E18))
Writer(42.0, Beginning of log
Called inner(42.0)
Called outer(1.73927494152050099E18))
As already mentioned in the comments, this whole construct is not a monad, it's rather something like "function composition in the monoid of Double
-endomorphisms with logging". There is no way to make your second example work (because Writer
is not a Double
, but your map
takes only Double => Double
), so I removed it.
Upvotes: 1