【问题标题】:Fighting IO when reading a configuration file into Haskell将配置文件读入 Haskell 时与 IO 战斗
【发布时间】:2020-05-18 14:53:59
【问题描述】:

我已经为我尚未编写的 Haskell 应用程序输入了数据,这些数据驻留在一个文件中。我不更新文件。我只需要读取文件并将其输入到我的 Haskell 函数中,该函数需要一个字符串列表。但是读取文件当然会产生IO 数据对象。我了解到使用<- 操作可以以某种方式“取出”包装在IO 结构中的字符串,所以我尝试了这个尝试:

run :: [String]
run = do
  datadef_content <- readFile "play.txt" -- yields a String
  let datadef = lines datadef_content -- should be a [String]
  return datadef

我把它放到一个文件 play.hs 中,然后通过 ghci 加载它

:l play

令我惊讶的是,我收到了readFile 行的错误消息

 Couldn't match type ‘IO’ with ‘[]’
 Expected type: [String]
   Actual type: IO String

对于return 的错误信息

 Couldn't match type ‘[Char]’ with ‘Char’
 Expected type: [String]
   Actual type: [[String]]

第一个似乎表明我无法摆脱IO,最后一条消息似乎表明lines 将返回一个字符串列表,这也没有意义我。

我怎样才能正确地做到这一点?

【问题讨论】:

  • 你确实无法摆脱IO(或者至少你应该尽量不要那样做)。有unsafeIO,但这通常会产生比它解决的更多的麻烦。
  • 不要尝试run :: [String]。相反,采用函数foo :: [String] -&gt; blah,它将run 的内容作为参数,并根据需要使用fmap(=&lt;&lt;) 将其转换为wrappedFoo :: IO [String] -&gt; blah,然后将其应用于run :: IO [String]
  • Haskell 强制你声明你执行 IO in type,如果你真的执行 IO。声明run :: [String] 等于承诺run 没有IO,并且是一个“纯”常量字符串列表。如果你想做IO,你需要声明run :: IO [String],告诉编译器这依赖于IO,并且可以多次执行,可能有不同的结果(如果"play.txt"改变了它的内容)。跨度>

标签: haskell io monads file-read do-notation


【解决方案1】:

您将run 声明为[String] 值。但是return不是提供函数返回值的关键字;它一个函数,类型为Monad m =&gt; a -&gt; m areturn datadef 产生一个 IO [String] 类型的值,它成为函数的返回值。

解决方案是为run提供正确的返回类型:

run :: IO [String]
run = do
    ...

run 也可以更简洁地定义为

run = fmap lines (readFile "play.txt")

尽管do 语法建议是,但没有办法从IO 操作中提取值out;你所能做的就是“推动”对lines的调用进入动作。

【讨论】:

  • 谢谢;我会试试看。您是否碰巧知道我可以了解fmap 含义的来源?我能用谷歌搜索的最好的东西是this description of Data.Functor,它非常简洁,我不太明白它是如何解释你的例子的。
  • 我试过你的方法。现在run 的声明很好,但这是否意味着我必须使用do 块内的文件内容进行所有计算?
  • 我发现this 网页似乎回答了我的问题。
猜你喜欢
  • 2015-09-27
  • 2011-09-08
  • 1970-01-01
  • 2021-03-24
  • 1970-01-01
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多