鉴于以下计划,我遇到了与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
答案 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”字样,例如runST
,runCont
或runEval
。但是,对于某些monad来说,打开它们可能并不安全(因此需要不同的unwrapppers)。
列表的一个实现是head
。但是如果列表是空的怎么办? Maybe
的解包器是fromJust
,但如果是Nothing
怎么办?
同样,Either
monad的解包器将类似于:
fromRight :: Either a b -> b
fromRight (Right x) = x
但是这个unwrapper并不安全:如果你有一个Left
值呢? (左侧通常表示错误状态,在您的情况下,是一个解析错误)。因此,对Either
值采取行动的最佳方式是使用either
函数,或者使用匹配Right
和Left
的案例陈述,正如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更具特色的功能-函数是一等值:
您可以使用其他类型的值之类的函数,例如如Bool
,Char
,Int
,Integer
等:
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 ML,OCaml和其他类似语言-谁知道,也许您会更乐于使用它们...