Развертывание монады

Учитывая приведенную ниже программу, у меня возникли проблемы, связанные с монадами.

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 и другие подобные языки - кто знает, может быть, вы будете счастливее их использовать...

Другие вопросы по тегам