Извлечение значения Maybe в IO
учитывая следующее:
> (liftM2 fromMaybe) (ioError $ userError "OOPS") (return $ Just "ok")
ghci дает мне
*** Exception: user error (OOPS)
конечно, fromMaybe работает правильно:
> (liftM2 fromMaybe) (return $ "not me") (return $ Just "ok")
"ok"
но кажется, что операция ввода-вывода выполняется, а затем отбрасывается:
> (liftM2 fromMaybe) (putStrLn "computing.." >> "discarded") (return $ Just "ok")
computing..
"ok"
почему это происходит? Есть ли способ сделать монаду ИО более ленивой?
в частности, учитывая value :: IO (Maybe a)
что такое (чистый, краткий) способ сказать
result <- (liftM2 fromMaybe) err value
и распаковать результат или бросить IOError соответственно?
3 ответов
Я не знаю, что делать IO
lazier-правильное направление здесь. То, что вы, кажется, хотите сделать, это сначала добраться до Maybe
, а затем устранить ее. Это можно написать несколькими способами, вот один вариант:
test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)
если перевести с liftM2
to do-notation, очевидно, почему ваш код терпит неудачу:
do x <- ioError $ userError "OOPS"
y <- return $ Just "ok"
return $ fromMaybe x y
это никогда не пройдет мимо первой строки, так как это безоговорочно бросает исключение.
Антония будет работать нормально, но если вы не заботитесь о конкретном исключении, вы также можете использовать сопоставление шаблонов:
do Just result <- value
если шаблон не соответствует, это вызовет fail
, что в случае IO
монады выдать исключение.
> Just x <- return Nothing
*** Exception: user error (Pattern match failure in do expression at <interactive>:1:0-5)
что такое (чистый, краткий) способ ... распаковать [результат] или бросить IOError соответственно?
я рекомендую вам не полагаться на метания ошибки. Вместо этого обработайте" ошибку " явно:
maybeM :: Monad m => m b -> (a -> m b) -> m (Maybe a) -> m b
maybeM err f value = do
x <- value
case x of
Just y -> f y
Nothing -> err
-- This can be written simply as:
maybeM err f value = do
x <- value
maybe err f x
-- or even shorter! This is starting to look like Anthony's answer :)
maybeM err f value = value >>= maybe err f
входные сигналы и типы функции должны говорить сами за себя. Вы используете его, давая ему действие для выполнения для Nothing
case или функция для выполнения значения внутри для Just
случае. Для ваших конкретных входов это будет посмотрите, как:
maybeM (ioError $ userError "OOPS") return (return $ Just "ok")
если вы обязательно, то "краткий способ распаковать результат или бросить IOError" будет:
-- compare to fromJust, a function to be avoided
fromJustIO :: IO (Maybe a) -> IO a
fromJustIO = maybeM (ioError $ userError "OOPS") return
обратите внимание, как подпись типа для это практически Maybe a -> a
, в чем суть magicMonadUnwrap :: Monad m => m a -> a
, что должно вызвать некоторые красные флаги. Однако вы можете использовать это злодеяние простым способом:
result <- fromJustIO value
хотя опять же, я настоятельно рекомендую использовать исключения здесь. Пытаться обработка ошибок более элегантным способом, чем просто взрыв, с помощью maybeM
и обеспечивая ИО действие в случае отказа.