I have an idea what monads are and I know how useful they are at "micro
level" (i.e. collection/option handling inside methods), but I'm still
struggling to understand how they can be used in the bigger picture.
Googling along, I came across this Haskell example for entering strong
passwords involving transformers, and I've tried to port it to scalaz7
(not knowing any Haskell at all).
http://en.wikibooks.org/wiki/Haskell/Monad_transformers
Here's my translation attempt:
<snip>
def isValidPassword(s: String) =
s.length >=8 && s.exists(_.isLetter) &&
s.exists(_.isDigit) && s.exists(!_.isLetterOrDigit)
type IOOpt[A] = OptionT[IO, A]
def getValidPasswordT: IOOpt[String] =
for(
s <- readLn.transLift[OptionT];
if(isValidPassword(s))
) yield s
def askPasswordT: IOOpt[String] =
for(
_ <- putStrLn("Insert your new password:").transLift[OptionT];
p <- getValidPasswordT;
_ <- putStrLn("Storing password " + p).transLift[OptionT]
) yield p
</snip>
(Any corrections and improvements are welcome.)
But this workflow is not realistic at all - usually I'd give three
attempts to enter a valid password and then declare failure, or
something like that. In the Haskell example, they mention repeating the
input until a valid password is entered:
msum $ repeat getValidPassword
But I don't really get how this works. My understanding is that the
repeat creates a lazy stream of getValidPassword instances, and the msum
"adds" them together, but I don't get how this ever terminates - is it
the way mplus is defined over Maybe that makes this magic happen?
I don't have the slightest idea how to express this in scala/scalaz,
neither how to modify it to "loop" three times only before giving up.
(And experimenting with this is no fun, especially in Eclipse, since I
seem to manage to produce a compiler stack overflow every 30 seconds.)
Of course I could do this "imperatively" from the outside:
var p: Option[String] = None
while(p.isEmpty) {
p = askPasswordT.runT.unsafePerformIO
}
...but this somehow defeats the purpose. :/
Any help/insight is appreciated, including pointers to helpful web links
or books. (Note that I'm not attached to this specific example or monad
transformers, I'm also interested to understand how monadic design works
"in the large" in general, i.e. above method body level.)
Best regards,
Patrick
Are you trying to understand monad transformers? The msum function? I
would shy away fro using IO for pedagogical purposes. I started typing
an answer to your questions, but then I started wondering what your
intention is.
--
Tony Morris
http://tmorris.net/
> def getNextInput: Input = ...
> def isValid(i: Input) = ...
>
> val response: Option[Input] =
> Stream.continually(getNextInput).take(3).find(isValid)
Thanks. Yes, a stream is probably part of the solution (the repeat
invocation in the Haskell example), but I'm still struggling to
understand how the transformer members of this stream are glued together
in the msum part. (I'm not looking for a way to rewrite the password
example in a different style, I'd rather like to understand what's going
on in the Haskell and get to a more or less faithful Scala translation.)
Best regards,
Patrick
> Are you trying to understand monad transformers? The msum function? I
> would shy away fro using IO for pedagogical purposes. I started typing
> an answer to your questions, but then I started wondering what your
> intention is.
Initially I was looking for accessible, simple examples for how monads
can be used to structure programs above method/function level in
general. Googling about, I came across a blog post on transformers[1]
and thought that this sounded like something in the right direction, so
I looked for more, small examples where the monadic stuff is passed
between functions and came across the Haskell password example[2] that
incidentally used the IO monad. If there's other directions I should
look at first when it comes to understanding how monads fit into overall
program design, I'll happily put away transformers to revisit them later.
That being said, my concrete problem with the given example was that I
had no idea how getValidPasswordT, repeat and msum interact to arrive at
termination on a non-empty Option. I read the Haskell code as
getValidPasswordT.repeat.msum
where repeat gives me an infinite, lazy list of OptionT[IO,A], and msum
"adds" together the members of this list. Now I have looked up the
definition of MaybeT's mplus[3], and the "short circuit" evaluation
there (return if first eval is Just, otherwise move on to second eval)
at least partially explains the question of the termination condition.
However, I still don't get what exactly is happening there - naively
trying to rewrite this, I get something like
seq.foldRight(noneT[IO,A]) { (cur: IOOpt[A], aggr: IOOpt[A]) =>
optionT(for(a <- aggr.runT) yield (a match {
case None => ???
case Some(_) => a
}))
}
...and I really don't see how to fit the "runMaybeT y" into the picture
- how would I get another Option[A] out of cur within the yield? (Not
knowing my way around Haskell syntax probably doesn't help much...)
Long story short, my main intention is to learn more about usage
patterns for monads in the context of a full application rather than a
single function/method, I'd appreciate any pointers to tutorials, books
or beginner-digestable sample code. And, now that I somehow ended up
chewing on this example, I'd like to understand what's happening there,
just for the peace of mind, even if I should start somewhere else and
ignore transformers for the time being. I'll keep trying to figure out
myself, but any hints are welcome.
Best regards,
Patrick
[1] http://debasishg.blogspot.com/2011/07/monad-transformers-in-scala.html
[2] http://en.wikibooks.org/wiki/Haskell/Monad_transformers
[3]
http://hackage.haskell.org/packages/archive/transformers/0.1.4.0/doc/html/src/Control-Monad-Trans-Maybe.html
Just watched your presentation on the reader monad[1], and it left me
with the same questions. (As one commenter on your blog already noted,
having shown an actual application of the concept perhaps might have
helped a bit.)
Let's say I reduce the Configuration in your example to...
case class Configuration(number: Int)
...and add...
object ConfigReader {
def fromConfig[A](fun: Configuration => A) = new ConfigReader[A] {
def apply(config: Configuration) = fun(config)
}
implicit def valueToNullConfigReader[A](value: => A) =
new ConfigReader[A] {
def apply(config: Configuration) = value
}
}
...then I can do stuff like...
val fun = (a: Int) => (b: Int) => (c: Int) => a + b + c
val lifted = lift3ConfigReader(fun)
println(lifted(1)(fromConfig(_.number))(3)(Configuration(10)))
...or directly...
val res = for(
a <- 1;
b <- fromConfig(_.number);
c <- 3
) yield a + b + c
println(res(Configuration(10)))
Is that intended usage? (If it is, how do I handle ambiguous implicits
for my "pure" values - or "pure" values that are monadic already, like
lists? Do I always explicitly have to instantiate the monad wrappers?)
But my main question is: That's flat, what about nested function calls?
If calculation of c requires the value of b, but only at one single
point some calls below - do I still have have to explicitly pass config
(or b) all the way down, or are there patterns to handle this? I.e., can
I somehow get a "dynamic variable" effect through the reader monad? And
what about interaction with other monads - what should my program look
like if I want to handle a database transaction (in another reader
monad?) and error handling (either monad?) along with the config?
Best regards,
Patrick
[1] http://blog.tmorris.net/configuration-without-the-bugs-and-gymnastics/