Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unwrapping a monad

Tags:

haskell

Given the below program, I am having issues dealing with monads.

module Main 
where
import System.Environment
import System.Directory
import System.IO
import Text.CSV

--------------------------------------------------

exister :: String -> IO Bool
exister path = do
  fileexist <- doesFileExist path 
  direxist  <- doesDirectoryExist path
  return (fileexist || direxist )

--------------------------------------------------
slurp :: String -> IO String 
slurp path = do
  withFile path ReadMode (\handle -> do
                             contents <- hGetContents handle
                             last contents `seq` return contents )
--------------------------------------------------    
main :: IO ()
main = do
  [csv_filename] <- getArgs
  putStrLn (show csv_filename)
  csv_raw <- slurp csv_filename
  let csv_data = parseCSV csv_filename csv_raw

  printCSV csv_data -- unable to compile. 

csv_data is an Either (parseerror) CSV type, and printCSV takes only CSV data.


Here's the ediff between the working version and the broken version.

***************
*** 27,30 ****
    csv_raw <- slurp csv_filename
    let csv_data = parseCSV csv_filename csv_raw

!   printCSV csv_data -- unable to compile. 
\ No newline at end of file
--- 27,35 ----
    csv_raw <- slurp csv_filename
    let csv_data = parseCSV csv_filename csv_raw

!   case csv_data of 
!     Left error -> putStrLn $ show error
!     Right csv_data -> putStrLn $ printCSV csv_data
!     
!   putStrLn "done"
!       

reference: http://hackage.haskell.org/packages/archive/csv/0.1.2/doc/html/Text-CSV.html

like image 572
Paul Nathan Avatar asked Aug 22 '11 22:08

Paul Nathan


People also ask

What is the point of a Monad?

monads are used to address the more general problem of computations (involving state, input/output, backtracking, ...) returning values: they do not solve any input/output-problems directly but rather provide an elegant and flexible abstraction of many solutions to related problems.

What is a Monad in simple terms?

What is a Monad? A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.

Are monads composable?

Monads are not composable. This poses a problem, since composition is one of the foremost patterns in functional programming. However, many alternatives have been devised. One of the most common is the monad transformer.


3 Answers

Regarding monads:

Yes, Either a is a monad. So simplifying the problem, you are basically asking for this:

main = print $ magicMonadUnwrap v

v :: Either String Int
v = Right 3

magicMonadUnwrap :: (Monad m) => m a -> a
magicMonadUnwrap = undefined

How do you define magicMonadUnwrap? Well, you see, it's different for each monad. Each one needs its own unwrapper. Many of these have the word "run" in them, for example, runST, runCont, or runEval. However, for some monads, it might not be safe to unwrap them (hence the need for differing unwrappers).

One implementation for lists would be head. But what if the list is empty? An unwrapper for Maybe is fromJust, but what if it's Nothing?

Similarly, the unwrapper for the Either monad would be something like:

fromRight :: Either a b -> b
fromRight (Right x) = x

But this unwrapper isn't safe: what if you had a Left value instead? (Left usually represents an error state, in your case, a parse error). So the best way to act upon an Either value it is to use the either function, or else use a case statement matching Right and Left, as Daniel Wagner illustrated.

tl;dr: there is no magicMonadUnwrap. If you're inside that same monad, you can use <-, but to truly extract the value from a monad...well...how you do it depends on which monad you're dealing with.

like image 162
Dan Burton Avatar answered Nov 15 '22 22:11

Dan Burton


Use case.

main = do
    ...
    case csv_data of
        Left  err -> {- whatever you're going to do with an error -- print it, throw it as an exception, etc. -}
        Right csv -> printCSV csv

The either function is shorter (syntax-wise), but boils down to the same thing.

main = do
    ...
    either ({- error condition function -}) printCSV csv_data
like image 35
Daniel Wagner Avatar answered Nov 15 '22 22:11

Daniel Wagner


You must unlearn what you have learned.

Master Yoda.

Instead of thinking about, or searching for ways to "free", "liberate", "release", "unwrap" or "extract" normal Haskell values from effect-centric (usually monadic) contexts, learn how to use one of Haskell's more distinctive features - functions are first-class values:

  • you can use functions like values of other types e.g. like Bool, Char, Int, Integer etc:

    arithOps :: [(String, Int -> Int -> Int)]
    arithOps =  zip ["PLUS","MINUS", "MULT", "QUOT", "REM"]
                    [(+), (-), (*), quot, rem] 
    

For your purposes, what's more important is that functions can also be used as arguments e.g:

map          :: (a -> b) -> [a] -> [b]
map f xs     =  [ f x | x <- xs ]

filter       :: (a -> Bool) -> [a] -> [a]
filter p xs  =  [ x | x <- xs, p x ]

These higher-order functions are even available for use in effect-bearing contexts e.g:

import Control.Monad

liftM  :: Monad m => (a -> b)           -> (m a -> m b)
liftM2 :: Monad m => (a -> b -> c)      -> (m a -> m b -> m c)
liftM3 :: Monad m => (a -> b -> c -> d) -> (m a -> m b -> m c -> m d)

...etc, which you can use to lift your regular Haskell functions:

do     .
       .
       .
   val <- liftM3 calculate this_M that_M other_M
       .
       .
       .

Of course, the direct approach also works:

do     .
       .
       .
   x <- this_M
   y <- that_M
   z <- other_M
   let val =  calculate x y z
       .
       .
       .

As your skills develop, you'll find yourself delegating more and more code to ordinary functions and leaving the effects to a vanishingly-small set of entities defined in terms of functors, applicatives, monads, arrows, etc as you progress towards Haskell mastery.


You're not convinced? Well, here's a brief note of how effects used to be handled in Haskell - there's also a longer description of how Haskell arrived at the monadic interface. Alternately, you could look at Standard ML, OCaml, and other similar languages - who knows, maybe you'll be happier with using them...

like image 3
atravers Avatar answered Nov 15 '22 20:11

atravers