Либо монада в IO монада - простой идиоматический c способ? - PullRequest
0 голосов
/ 18 апреля 2020

Этот код компилируется:

read :: IO Config
read = do
  c1 <- BS.readFile "my_config.yaml"
  case Y.decodeEither' c1 of
    Right x -> pure x
    Left e -> error "error 123"

, где это не так:

read :: IO Config
read = do
  liftIO $ case (BS.readFile "my_config.yaml") >>= Y.decodeEither' of
    Right x -> pure x
    Left e -> error "error 123"

===>

  Expected type: IO Config
    Actual type: Either a1 Config

Как я могу преобразовать любую монаду к одному? Я бы хотел простой идиоматический способ c без дополнительных библиотек.

Ответы [ 3 ]

3 голосов
/ 18 апреля 2020

Строка BS.readFile "my_config.yaml" >>= Y.decodeEither' не имеет большого смысла, так как сама decodeEither' :: FromJSON a => ByteString -> Either ParseException a не возвращает IO a, а BS.readFile не является Either ParseException b, поэтому он не имеет ни контекста IO monadi c, ни контекста Either a monadi c.

Вы можете выполнить отображение функтора для результата, поэтому Y.decodeEither' <$> BS.readFile "my_config.yaml", но тогда он имеет тип FromJSON a => IO (Either ParseException a), поэтому вы не можете использовать сопоставление с шаблоном Left и Right для этого.

Однако вы можете реализовать это как:

read :: IO Config
read = do
    result <- <b>Y.decodeEither <$></b> BS.readFile "my_config.yaml"
    case <b>result</b> of
        Right x -> pure x
        Left e -> error e

или вы можете, как @ JosephSible говорит выполнить отображение в case части:

read :: IO Config
read = do
    result <- BS.readFile "my_config.yaml"
    case <b>Y.decodeEither result</b> of
        Right x -> pure x
        Left e -> error e
1 голос
/ 18 апреля 2020
read :: IO Config
read = do
  liftIO $ case (BS.readFile "my_config.yaml") >>= Y.decodeEither' of
    Right x -> pure x
    Left e -> error "error 123"

В приведенной выше неверной попытке это выглядит почти так, как вы хотите -XLambdaCase:

    fmap Y.decodeEither' (BS.readFile "my_config.yaml) >>= \case
        Right x -> pure x
        Left e -> error "error 123"

Но я нахожу either более читабельным:

    either (error "error 123") pure . Y.decodeEither' =<< BS.readFile "my_config.yaml"
1 голос
/ 18 апреля 2020

Нет необходимости использовать do или liftIO здесь.

read :: IO Config
read = Y.decodeEither' <$> BS.readFile "my_config.yaml" >>= \c -> case c of
                                                                  Right x -> pure x
                                                                  Left  e -> error "error 123"

должно это сделать.

Однако было бы лучше использовать трансформатор ExceptT тогда все, что вам нужно сделать, это

read :: ExceptT ParseException IO Config
read =  ExceptT $ Y.decodeEither <$> BS.readFile "my_config.yaml"

Теперь вы можете делать как;

configure :: ExceptT ParseException IO ()
configure =  read >>= pure . processConfig

или, как напоминает @Joseph Sible-Reinstate Monica, например

configure :: ExceptT ParseException IO ()
configure = processConfig <$> read

и если decodeEither возвращает значение Left, оно регистрируется и processConfig :: Config -> () пропускается.

...