【问题标题】:Haskell writeFileHaskell 写入文件
【发布时间】:2018-09-12 08:38:46
【问题描述】:

初学者。有一个名为 HHtml 的模块输出以下内容:

  setDoc = "<!DOCTYPE = <html><head>"
  setTitle = "<title>" ++ htmlTitle generator ++ "</title>"
  setHeader = "<header>" ++ htmlHeader generator ++ "</header>"
  setMeta = "<meta>" ++ htmlMeta generator ++ "</meta></head>"
  setBody = "<body>" ++ htmlBody generator ++ "</body>"
  setFooter = "<footer>" ++ htmlFooter generator ++ "</footer>"
  setEOF = "</html>"

  setHTML = [setDoc, setTitle, setHeader, setMeta, setBody, setFooter, setEOF]

主文件:

import HHtml
import System.IO

main = do
  let content = mapM_ putStrLn setHTML
  writeFile "index.html" content

现在,无论我怎么看,我都会不断收到Couldn't match type IO() with [Char] 或任何变体。我理解错误消息,但我对修复它感到非常困惑。谢谢指点!

【问题讨论】:

  • 修复取决于您要达到的目标。这段代码应该做什么?
  • 输出 (writeFile) 使用 mapM_ 在列表 setHTML 上创建的 html。 setDoc、setTitle 等只是占位符
  • 你用mapM_做什么?
  • 因为最终列表中的值将是 IO 摄入量
  • 只是为了确保 - 如果您将其用于生产,请确保您控制输入或进行一些转义以避免 JS 注入

标签: haskell types io


【解决方案1】:

mapM_ putStrLn setHTMLIO () 类型的操作,您通过 let 语句将其分配给名称 content。执行时,此操作将打印 setHTML 的每一行,不返回任何内容。您可以通过编写如下代码来执行此操作:

main = do
  let content = mapM_ putStrLn setHTML
  content

没有变量,这只是:

main = mapM_ putStrLn setHTML

但是content 是一个不透明的值——你可以用它做的唯一事情是从main执行它,用&gt;&gt;= 将它加入到其他IO 操作中(或do 表示法),并将其存储在数据结构中(此处不需要)。特别是,它不“存储”页面的内容,它只是向运行时描述它应该如何打印该内容。无论如何,您注意到的类型不匹配:writeFile 接受 String,也就是 [Char],这显然不是 IO ()

但是由于您显然想使用writeFilesetHTML 的每一行写入文件,而不是标准输出,因此您不想要打印行的操作 - 您需要行本身,加入与换行符一起。有几种可能的方法可以做到这一点,具体取决于您希望如何扩展此代码。

一种方法是使用unlines :: [String] -&gt; String 函数将行与换行符连接在一起,然后使用writeFile 将生成的String 写入"index.html"

main = writeFile "index.html" (unlines setHTML)

如果你想将拼接后的内容放在一个变量中,当然可以这样做:

main = do
  let content = unlines setHTML
  writeFile "index.html" content

(实际上,如果您不需要 setHTML 成为 [String],则可以将 unlines 调用移动到 setHTML 的定义中。)

现在writeFile 将接受content,因为它是String 值,而不是IO () 操作。这是一个很好的方法,因为它保持构建页面的逻辑纯粹,并且只根据需要使用IO来实际编写页面。

或者,您可以采取更强制的方法,留在IO。那么一个好用的函数是withFile(来自System.IO),它有以下类型:

FilePath -> IOMode -> (Handle -> IO r) -> IO r

需要FilePath 才能打开,IOMode(例如ReadModeWriteMode)来指示您是读取还是写入句柄,以及一个函数 接受句柄并执行一些IO 并返回某种类型的结果r;它返回一个IO 操作,用于打开文件、运行您的函数、自动确保文件已关闭(即使抛出异常)并返回结果。

然后您将使用 mapM_ 以与您已有的方式类似的方式将每一行打印到该句柄 - 为此,hPutStrLn :: Handle -&gt; String -&gt; IO () 写入特定句柄,而不是 putStrLn 写入标准输出。精简版:

main = withFile "index.html" WriteMode $ \file -> do
  mapM_ (hPutStrLn file) setHTML

如果您不喜欢 lambda 的外观,可以使用更详细的版本:

main = withFile "index.html" WriteMode writeContents
  where writeContents file = mapM_ (hPutStrLn file) setHTML

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    • 2017-01-30
    • 1970-01-01
    相关资源
    最近更新 更多