使用Monad读取INI文件

3

我想使用单子(monads)来读取一个INI文件的Haskell代码。

以下是我的代码:

import Control.Monad
import Data.Ini

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

在我的有限理解中,configport的类型应该是Either。如何在IO操作中使用Either值?

你知道如何在没有 IO 操作的情况下使用 Either 值吗? - Cubic
我尝试使用此处突出显示的 case of 语法:https://pastebin.com/nquCFvck,并且似乎有效。现在正在尝试使用 bind 进行学习。 - Ajit
2
不,那是(或者更确切地说:一个)正确的解决方案。你可以使用eitherfromRight函数,但我认为这不会有显著的改进。请注意,如果您使用Left分支终止程序(带有错误或异常),并且右分支返回值,则无需保持嵌套。 - Cubic
你不能使用bind来完成这个操作:大致上,如果将某个东西从其单子上下文中取出,然后再将其重新插入到相同的单子上下文中,那么bind就会起作用。但是你不能使用bind将Either a中的内容取出并放入IO中。 - chi
2个回答

4

由于涉及到多个monad,您可以使用monad transformer。尽管您预计Either monad transformer应该被称为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

通过执行各种操作,上述代码示例中的config是一个Ini值,而port是一个Text值。

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


1
说到“各种原因”,我要补充一下,你可能会看到使用 ErrorT 的旧代码,它有一个虚假的 Error 约束,或者 (旧版本的?) either 中的 EitherT,但是 ExceptT 是现在正确的 Either 转换器的选择。 - Jon Purdy

2
有两种不同的单子(monads)。`readIniFile` 返回一个 `IO (Either String Ini)` 值,这意味着 `config` 的类型是 `Either String Ini`。在这个 `do` 表达式中,你不能从 `config >>= lookupValue "NETWORK" "port"` 返回的 `Either String Text` 值中提取 `Text` 值。相反,使用 `either` 来返回默认值,如果查找失败,或者如果查找成功,则提取端口。请注意保留 HTML 标签。
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 >>= ...)。 (我认为需要导入 Data.Either,但我目前无法确认。) - chepner
2
是的,在这种情况下问题在于它同时做了两件事,这对于示例来说很好,但在“真实”的代码中并不是那么好 - 配置值不存在通常不是错误(除非确实不存在合理的默认值),但您肯定希望将配置文件中的语法错误视为错误。 - Cubic
Either 真的是一个单子吗? - bipll
1
@bipll Either 不是,但 Either String 是。 - chepner
@Cubic 很好的建议;我会更新一个帮助函数,以区分缺少 port 键和其他错误。 - chepner
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接