打开一个monad

时间:2011-08-22 22:48:27

标签: haskell

鉴于以下计划,我遇到了与monad打交道的问题。

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是一种Either(parseerror)CSV类型,而printCSV只接受CSV数据。


这是工作版和破碎版之间的编辑。

***************
*** 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"
!       

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

3 个答案:

答案 0 :(得分:9)

关于monads:

是的,Either a是一个单子。所以简化问题,你基本上要求这个:

main = print $ magicMonadUnwrap v

v :: Either String Int
v = Right 3

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

您如何定义magicMonadUnwrap?嗯,你看,每个monad都不一样。每个人都需要自己的解包装。其中许多都包含“run”字样,例如runSTrunContrunEval。但是,对于某些monad来说,打开它们可能并不安全(因此需要不同的unwrapppers)。

列表的一个实现是head。但是如果列表是空的怎么办? Maybe的解包器是fromJust,但如果是Nothing怎么办?

同样,Either monad的解包器将类似于:

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

但是这个unwrapper并不安全:如果你有一个Left值呢? (左侧通常表示错误状态,在您的情况下,是一个解析错误)。因此,对Either值采取行动的最佳方式是使用either函数,或者使用匹配RightLeft的案例陈述,正如Daniel Wagner所说。

tl; dr :没有magicMonadUnwrap。如果你在同一个monad中,你可以使用<-,但要真正从monad中提取值...嗯......你怎么做取决于你正在处理的monad。

答案 1 :(得分:7)

使用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

either函数更短(语法方面),但归结为同样的事情。

main = do
    ...
    either ({- error condition function -}) printCSV csv_data

答案 2 :(得分:1)

您必须取消学习所学的知识。

尤达大师。

不是在思考或寻找从“以效果为中心”(通常是单子)的上下文中“释放”,“解放”,“释放”,“解包”或“提取”纯值的方法,而是学习如何使用以下方法之一Haskell更具特色的功能-函数是一等值

  • 您可以使用其他类型的值之类的函数,例如如BoolCharIntInteger等:

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

出于您的目的,更重要的是函数也可以用作参数,例如:

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 ]

这些高阶函数甚至可以用于具有效果的上下文中,例如:

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,可用于提升纯功能:

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

当然,您也可以直接执行以下操作:

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

随着技能的发展,您会发现自己将越来越多的代码委派给纯函数,并将效果留给了逐渐消失的很小的一组实体,这些实体由函子,应用函数,单子,箭头等定义,向着前进Haskell精通。


您不相信吗?好吧,here's简要说明了Haskell过去如何处理效果-there's还对Haskell如何到达单子界面进行了较长的描述。或者,您可以查看Standard MLOCaml和其他类似语言-谁知道,也许您会更乐于使用它们...