Есть ли способ развернуть тип из монады ввода-вывода?

StackOverflow https://stackoverflow.com//questions/22063216

  •  23-12-2019
  •  | 
  •  

Вопрос

У меня есть эта очень простая функция

import qualified Data.ByteString.Lazy as B

getJson :: IO B.ByteString
getJson = B.readFile jsonFile

readJFile :: IO (Maybe Response)
readJFile =  parsing >>= (\d ->
             case d of
                 Left err -> return Nothing
                 Right ps -> return (Just ps))
    where parsing = fmap eitherDecode getJson :: IO (Either String Response)

где jsonFile это путь к файлу на моем жестком диске (прошу прощения за отсутствие обозначения do, но я нашел это более понятным для работы)

мой вопрос заключается в следующем;есть ли для меня способ избавиться от IO часть, чтобы я мог работать с байтовой строкой в одиночку?

Я знаю, что вы можете сопоставлять шаблоны для определенных монад, таких как Either и Maybe чтобы получить их значения, но можете ли вы сделать что-то подобное с IO?

Или озвученный по - другому:есть ли у меня способ сделать readJFile Возврат Maybe Response без ввода-вывода?

Это было полезно?

Решение

Чтобы подробнее остановиться на моих комментариях, вот как вы можете это сделать:

getJson :: IO B.ByteString
getJson = B.readFile jsonFile -- as before

readJFile :: B.ByteString -> Maybe Response -- look, no IO
readJFile b = case eitherDecode b of
                Left err -> Nothing
                Right ps -> Just ps

В конце концов, вы снова объединяете все в одно действие ввода-вывода:

getAndProcess :: IO (Maybe Response)
getAndProcess = do
  b <- getJson
  return (readJFile b)

Другие советы

Вам никогда не нужно "перетаскивать монаду" через какие-либо функции, если только все они на самом деле не должны выполнять ввод-вывод.Просто поднимите всю цепочку в монаду с помощью fmap (или liftM / liftM2 / ...).

Например,

f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M

и все ваше дело должно быть таким

m :: M
m = let k = "f1 getJson"
    in f4 (f2 k) (f3 k)

То, что вы можете просто сделать

m = fmap (\b -> let k = f1 b
                in f4 (f2 k) (f3 k) )
    getJson

Кстати, это могло бы выглядеть приятнее с do обозначение:

m = do
  b <- getJson
  return $ let k = f1 b
           in f4 (f2 k) (f3 k)

Относительно вашей правки и вопроса

есть ли у меня способ сделать readJFile Возврат Maybe Response без IO?

НЕТ, это никак не может сработать, потому что readJFile действительно нужно сделать ввод-вывод.Нет никакого способа убежать от IO тогда монада, вот в чем весь смысл!(Ну, есть unsafePerformIO как говорит Рикардо, но это определенно не является действительным приложением для этого.)

Если дело в неуклюжести распаковки Maybe значения в IO монада и подписи с скобками в них, вы можете захотеть взглянуть на MaybeT трансформатор.

readJFile' :: MaybeT IO Response
readJFile' = do
   b <- liftIO getJson
   case eitherDecode b of
     Left err -> mzero
     Right ps -> return ps

В общем, да, способ есть.Сопровождается множеством "но", но есть.Вы спрашиваете, как это называется небезопасная операция ввода-вывода: System.IO.Небезопасный.Обычно он используется для написания оболочек при вызове внешних библиотек, к нему не приходится прибегать в обычном коде Haskell.

В принципе, вы можете позвонить unsafePerformIO :: IO a -> a который делает именно то, что вы хотите, он удаляет IO часть и возвращает вам завернутое значение типа a.Но, если вы посмотрите на документацию, есть ряд требований, которые вы должны сами гарантировать системе, и все они сводятся к одной и той же идее:даже если вы выполнили операцию через IO, ответ должен быть результатом функции, как и ожидалось от любой другой функции haskell, которая не работает в IO:он всегда должен давать один и тот же результат без побочных эффектов, только на основе входных значений.

Здесь, учитывая ваш код, очевидно , что это НЕ тот случай, поскольку вы читаете из файла.Вы должны просто продолжить работу внутри монады ввода-вывода, вызвав свой readJFile из другой функции с типом результата IO something.Затем вы сможете прочитать значение в IO оболочка (находящаяся в IO самостоятельно), поработайте над этим, а затем повторно оберните результат в другой IO при возвращении.

Нет, безопасного способа получить значение из монады ввода-вывода не существует.Вместо этого вы должны выполнять работу внутри монады ввода-вывода, применяя функции с помощью fmap или bind (>>=).Также вам следует использовать decode вместо eitherDecode, если вы хотите, чтобы ваш результат был в Maybe .

getJson :: IO B.ByteString
getJson = B.readFile jsonFile

parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode

readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON

Вы также могли бы использовать обозначение do, если это вам понятнее:

readJFile :: IO (Maybe Response)
readJFile = do
    bytestring <- getJson
    return $ decode bytestring

Обратите внимание, что вам даже не нужна функция parseResponse, поскольку readJFile указывает тип.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top