Развертывание монады
Учитывая приведенную ниже программу, у меня возникли проблемы, связанные с монадами.
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 является CSV-типом Either (parseerror), а 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
2 ответа
Использование 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
Что касается монад:
Да, Either a
это монада Итак, упрощая проблему, вы в основном просите об этом:
main = print $ magicMonadUnwrap v
v :: Either String Int
v = Right 3
magicMonadUnwrap :: (Monad m) => m a -> a
magicMonadUnwrap = undefined
Как вы определяете magicMonadUnwrap
? Ну, вы видите, это отличается для каждой монады. Каждому нужен свой собственный распаковщик. У многих из них есть слово "бежать", например, runST
, runCont
, или же runEval
, Тем не менее, для некоторых монад, возможно, небезопасно их разворачивать (отсюда и необходимость в разных распаковщиках).
Одна реализация для списков будет head
, Но что, если список пуст? Развертывание для Maybe
является fromJust
, но что если это Nothing
?
Аналогично, распаковщик для Either
Монада будет что-то вроде:
fromRight :: Either a b -> b
fromRight (Right x) = x
Но этот распаковщик небезопасен: что, если у вас был Left
значение вместо? (Слева обычно представляет состояние ошибки, в вашем случае, ошибка разбора). Так что лучший способ действовать на Either
значение это использовать either
функция, или использовать сопоставление оператора case Right
а также Left
Как показал Даниэль Вагнер.
tl; dr: нет magicMonadUnwrap
, Если вы внутри той же монады, вы можете использовать <-
, но по-настоящему извлечь значение из монады... ну... как вы это делаете, зависит от того, с какой монадой вы имеете дело.
Вы должны разучиться тому, чему научились.
Мастер Йода.
Вместо того чтобы думать или искать способы "освободить", "высвободить", "высвободить", "развернуть" или "извлечь" чистые значения из ориентированных на эффект (обычно монадических) контекстов, научитесь использовать один из наиболее характерных для Haskell features - функции являются первоклассными значениями:
вы можете использовать такие функции, как значения других типов, например, как
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)
... и т.д., которые вы можете использовать для поднятия ваших чистых функций:
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.
Вы не уверены? Что ж, вот краткая заметка о том, как раньше в Haskell обрабатывались эффекты - есть также более подробное описание того, как Haskell пришел к монадическому интерфейсу. В качестве альтернативы вы можете посмотреть на Standard ML, OCaml и другие подобные языки - кто знает, может быть, вы будете счастливее их использовать...