The essence of my monad confusion

On the wiki page for Applicative Functors (http://www.haskell.org/haskellwiki/Applicative_functor) a familiar characteristic of monads is quoted; that they "allow you to run actions depending on the outcomes of earlier actions". I feel comfortable with Functors and Applicative Functors, but I don't yet get that "extra power" that monads provide. An example immediately follows that quotation on the wiki: do text <- getLine if null text then putStrLn "You refuse to enter something?" else putStrLn ("You entered " ++ text) For simplicity's sake, I modified it to avoid using the IO monad; the "text" binding is now provided by the first parameter, and (=<<) is used due to its similarity to fmap: bar :: Monad m => m String -> m String bar as = (=<<) (\a -> if null a then return "nothing" else return "something") as This works fine, so bar ["Blah"] gives ["something"], and bar (Just "") gives ["nothing"]. But, I can get the same effect using a Functor (replacing (=<<) with fmap): bar2 :: Functor f => f String -> f String bar2 as = fmap (\a -> if null a then "nothing" else "something") as Can anyone help me out of the swamp? Cheers, Paul

On Sat, May 2, 2009 at 11:31 AM, Paul Keir
On the wiki page for Applicative Functors (http://www.haskell.org/haskellwiki/Applicative_functor) a familiar characteristic of monads is quoted; that they "allow you to run actions depending on the outcomes of earlier actions". I feel comfortable with Functors and Applicative Functors, but I don't yet get that "extra power" that monads provide.
An example immediately follows that quotation on the wiki:
do text <- getLine if null text then putStrLn "You refuse to enter something?" else putStrLn ("You entered " ++ text)
For simplicity's sake, I modified it to avoid using the IO monad; the "text" binding is now provided by the first parameter, and (=<<) is used due to its similarity to fmap:
bar :: Monad m => m String -> m String bar as = (=<<) (\a -> if null a then return "nothing" else return "something") as
This works fine, so bar ["Blah"] gives ["something"], and bar (Just "") gives ["nothing"].
But, I can get the same effect using a Functor (replacing (=<<) with fmap):
bar2 :: Functor f => f String -> f String bar2 as = fmap (\a -> if null a then "nothing" else "something") as
Can anyone help me out of the swamp?
Suppose I had functions: bar3 :: Functor f => String -> f Bool bar4 :: Funcotr f => Bool -> f Integer If the only thing I have is the functor constraint, to chain them together I have to end up with something of type (Functor f => String -> f (f Integer)). If I had a Monad constraint on bar3 and bar4 I could flatten the result type and get rid of the "double nesting" and end up with a function of type (String -> f Integer). So it's not about what a single function can do, but what tools I have to compose the functions. Antoine

Thanks for all the help. The simplified example indeed threw away too much. There were no side effects. Brent, of course I couldn't create your function; though I gained through trying. I then found it useful to consider the type of: fmap (\x -> putStrLn x) getLine which is IO (IO ()) and hence displays nothing to the screen. Felipe, your recursive example was also compelling and concise. Antoine, I see how the join capacity of a Monad can be useful in this issue. I'm also reminded of what <*> can bring to fmap/<$>. On reflection, I often trip up when learning by comparing IO to simpler monads such as [] and Maybe. But [] and Maybe never have effects, and so are poor foils. The ((->) t) monad is henceforth in my toolbox. Paul

On Sat, May 02, 2009 at 05:31:03PM +0100, Paul Keir wrote:
On the wiki page for Applicative Functors (http://www.haskell.org/haskellwiki/Applicative_functor) a familiar characteristic of monads is quoted; that they "allow you to run actions depending on the outcomes of earlier actions". I feel comfortable with Functors and Applicative Functors, but I don't yet get that "extra power" that monads provide.
An example immediately follows that quotation on the wiki:
do text <- getLine if null text then putStrLn "You refuse to enter something?" else putStrLn ("You entered " ++ text)
For simplicity's sake, I modified it to avoid using the IO monad; the "text" binding is now provided by the first parameter, and (=<<) is used due to its similarity to fmap:
bar :: Monad m => m String -> m String bar as = (=<<) (\a -> if null a then return "nothing" else return "something") as
This simplification does not preserve the crucial aspect of the example: (putStrLn "blah") has an effect (namely, printing something on the screen), whereas (return "blah") never has an effect (whatever "effect" means for the particular monad in question). You seem to be confusing the return value of a monadic computation with its side effects. In the case of 'putStrLn "blah"', the return value is (), and the side effect is to print "blah" to the screen; in the case of 'return "blah"', the return value is "blah" and there are no side effects. Since IO is a monad, we can compute the result of 'getLine' and then use it to decide which of the two putStrLns to run; the effect of the other one (printing something on the screen) will never happen. But there is no way to do this with only the Applicative interface. Try writing a function f :: IO String -> IO () -> IO () -> IO () which has the same behavior as the 'do text <- getLine...' example above, using *only* the Applicative instance of IO; you won't be able to do it. Depending how you implement f, it may execute only the first IO (), or only the second, or both, or neither; but it will always be the same. You won't be able to have f choose which to execute based on the result of the IO String parameter. -Brent

On Sat, May 02, 2009 at 05:31:03PM +0100, Paul Keir wrote:
An example immediately follows that quotation on the wiki:
do text <- getLine if null text then putStrLn "You refuse to enter something?" else putStrLn ("You entered " ++ text)
Then, how about getMyLine = getLine >>= \text -> if null text then getMyLine else return m Can you, using functors, decide to run 'getLine' again or not? HTH, -- Felipe.
participants (4)
-
Antoine Latter
-
Brent Yorgey
-
Felipe Lessa
-
Paul Keir