Monadic design (transformers)

57 views
Skip to first unread message

Patrick Roemer

unread,
Nov 11, 2011, 7:41:54 AM11/11/11
to scala...@googlegroups.com
Hi,

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

Naftoli Gugenheim

unread,
Nov 11, 2011, 1:00:09 PM11/11/11
to Patrick Roemer, scala...@googlegroups.com
Not sure if it's what you want, but recently I did something similar to this:

def getNextInput: Input = ...
def isValid(i: Input) = ...

val response: Option[Input] = Stream.continually(getNextInput).take(3).find(isValid)

Stream's elements are lazily evaluated a maximum of once. Stream.continually takes a call-by-name and returns an infinite Stream whose elements will be the result of that expression at the time that element must be evaluated. take limits it to a finite 3, and find returns the first one that's valid. If the first or second is valid, it will return that input and the rest of the stream will never be evaluated. If no answer is valid, None will be returned of course.

You could also just use recursion.

Tony Morris

unread,
Nov 11, 2011, 3:27:36 PM11/11/11
to scala...@googlegroups.com

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/


Patrick Roemer

unread,
Nov 11, 2011, 6:48:50 PM11/11/11
to scala...@googlegroups.com
Responding to Naftoli Gugenheim:

> 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

Patrick Roemer

unread,
Nov 11, 2011, 6:51:16 PM11/11/11
to scala...@googlegroups.com
Responding to Tony Morris:

> 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

Luc Duponcheel

unread,
Nov 12, 2011, 5:36:39 AM11/12/11
to Patrick Roemer, scala...@googlegroups.com
> ... but any hints are welcome ...


since you asked for it:
here you can find some examples using state and control transformers

http://imaginej.blogspot.com/

Luc
--
   __~O
  -\ <,
(*)/ (*)

reality goes far beyond imagination

Patrick Roemer

unread,
Nov 18, 2011, 6:45:06 AM11/18/11
to scala...@googlegroups.com
Responding to myself:

> Responding to Tony Morris:
>
>> 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.

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/

Reply all
Reply to author
Forward
0 new messages