
Mark T.B. Carroll wrote:
Hello! I am trying and failing to use one monad in another. A tiny example is enclosed, where I try to use IO to help along a State-ful computation. Is the real mistake in do_next? Is there a minimal rewrite that will get this working that I can use as a template for other things? Or, is there a good tutorial somewhere on this type of thing? Any pointers are much appreciated!
Combining monads isn't straightforward. There is a paper on the topic, at: http://cm.bell-labs.com/cm/cs/who/wadler/topics/monads.html#combining-monads However, you can't really combine the IO monad in the way that the above paper describes. The most direct way to fix your program is to use an IORef instead of a state monad, e.g.
import IORef
update_best_with :: IORef (String, Int) -> (String, Int) -> IO () update_best_with ref (current_name, current_count) = do (best_name, best_count) <- readIORef ref when (current_count > best_count) $ do writeIORef ref (current_name, current_count)
do_next :: IORef (String, Int) -> String -> IO () do_next ref name = do count <- get_length name update_best_with ref (name, count)
longest_file :: [String] -> IO (String, Int) longest_file names = do ref <- newIORef ("none", 0) mapM_ (do_next ref) names readIORef ref
A more elegant solution would be to generate a list of filename/length pairs (e.g. using mapM), then just use maximumBy to select the longest, e.g.
get_length' :: String -> IO (String, Int) get_length' name = do length <- get_length name return (name, length)
longest_file :: [String] -> IO (String, Int) longest_file names = do pairs <- mapM get_length' names return $ maximumBy cmp pairs where cmp (_, l1) (_, l2) = compare l1 l2
--
Glynn Clements