【问题标题】:Reading an INI file using monads使用 monad 读取 INI 文件
【发布时间】:2018-05-02 14:09:03
【问题描述】:

我正在尝试使用 monad 读取带有 haskell 的 INI 文件。

这是我的代码:

import Control.Monad
import Data.Ini

main = do
    config <- readIniFile "configs/config.ini"
    port <- (config >>= lookupValue "NETWORK" "port")
    port >>= putStrLn

在我有限的理解中,configport 的类型是 Either。如何将Either 值与IO 操作一起使用

【问题讨论】:

  • 您知道如何使用Either没有 IO 操作吗?
  • 我尝试了使用case of 语法,如下所示:pastebin.com/nquCFvck,它似乎有效。现在尝试将bind 用于学习目的
  • 不,这是(或者更确切地说:a)正确的解决方案。您可以改用eitherfromRight 函数,但我认为这不会是一个重大改进。请注意,如果您有 Left 分支终止程序(出现错误或异常)并且正确的分支返回值,则不需要继续嵌套。
  • 您不能为此使用 bind:粗略地说,如果将其重新插入到相同的 monadic 上下文中,bind 会从它的 monadic 上下文中取出一些东西。你不能从Either a 中取出一些东西,然后用bind 把它放到IO 中。

标签: haskell


【解决方案1】:

由于涉及多个 monad,您可以使用 monad 转换器。虽然您希望 Either monad 转换器被称为 EitherT,但由于各种原因,它被称为 ExceptT

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except
import Data.Ini

main = runExceptT $ do
  config <- ExceptT $ readIniFile "./config.ini"
  port <- ExceptT $ return $ lookupValue "NETWORK" "port" config
  liftIO $ putStrLn $ show port

通过各种提升,上面代码示例中的configIni值,portText值。

当所有操作都成功时,程序会打印port 值,但如果其中一个操作失败,则什么都不会发生。

【讨论】:

  • 说到“各种原因”,我要补充一点,你可能会看到使用 ErrorT 的旧代码,它有一个虚假的 Error 约束,或者来自(旧版本的?)的 EitherT either,但ExceptT 是当今Either 变压器的正确选择。
【解决方案2】:

涉及两个不同的 monad。 readIniFile 返回一个 IO (Either String Ini) 值,这意味着 config 的类型为 Either String Ini。使用 this do 表达式,您无法从 config &gt;&gt;= lookupValue "NETWORK" "port" 返回的 Either String Text 值中提取 Text 值。相反,如果查找失败,请使用either 返回默认值,或者如果查找成功则提取端口。

getPort :: Ini -> Either String Text
getPort cfg = let result = lookupValue "NETWORK" "port" cfg
              in case result of
                    Left "Couldn't find key: port" -> Right "0"
                    otherwise -> result

 main = do
  config <- readIniFile "configs/config.ini"
  -- You could probably do better than just raise an error
  let port = either error id (config >>= getPort)
  putStrLn (unpack port)

【讨论】:

  • 由于这只是忽略了lookupValue 可能返回的错误消息,因此您也可以使用fromRight "0" (config &gt;&gt;= ...)。 (我认为需要导入Data.Eithereither 不需要,但我目前无法确认。)
  • 我想知道嵌套的do 并继续使用&lt;- 是否会让代码避免&gt;&gt;= 并防止它忽略配置读取错误(如果有)。
  • @9000 可能不会没有引入 monad 转换器,其方式不如当前代码清晰。
  • 是的,在这种情况下的问题是同时做两件事,这对于一个例子来说很好,但在“真实”代码中不是很好——配置值不存在通常不是错误(除非确实不存在合理的默认值),但您肯定希望将配置文件中的语法错误视为错误。
  • @bipll Either 不是,但Either String 是。
猜你喜欢
  • 2011-07-24
  • 2013-04-18
  • 2015-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多