Return do 块中多个字符串中的单个字符串

Return a single string from multiple strings in a do block

我正在尝试实现类似于 HaTeX 中编程的东西,程序员可以将 LaTeX 命令逐行编写为文本。他们的例子之一如下:

-- Body with a section.
theBody :: Monad m => LaTeXT_ m
theBody = do
    maketitle
    section "Hello"
    "This is a simple example using the "
    hatex
    " library. "
    -- 'textbf' turns characters to bold font (as you already may know).
    textbf "Enjoy!"
    " "

完整示例: https://github.com/Daniel-Diaz/HaTeX/blob/master/Examples/simple.hs

到目前为止,我已经取得了以下成就:

module Main 
where import System.IO


writeContent :: String 
writeContent = do
    "Some text. "
    ++ "Some more text. "
    ++ "This should all compile into a single line "
    ++ "and be output to the screen."

main :: IO ()
main = do
    putStr $ writeContent

但我真的很想去掉 ++ 运算符。

我知道 Strings++Text 效率低,所以一旦我学到更多,最终会改变它。我对 Haskell 还是很陌生。我试过查看 HaTeX 源代码,但有些部分在一行中实现了太多步骤,所以想一次构建它的小步骤。

您可以使用 writer monad:

import Control.Monad.Trans.Writer

writeContent :: Writer String ()
writeContent = do
  tell "Some text. "
  tell "Some more text. "
  tell "This should all compile into a single line "
  tell "and be output to the screen."

main :: IO ()
main = do
  putStr $ execWriter writeContent

要在没有像 tell 这样的额外(可见)函数调用的情况下执行此操作,您需要 OverloadedStrings extension:

{-# LANGUAGE GADTs, GeneralizedNewtypeDeriving, OverloadedStrings
  #-}

import Control.Monad.Trans.Writer
import Data.String

newtype StringBuilder a =
  StringBuilder (Writer String a)
  deriving (Functor, Applicative, Monad)

instance (a ~ ()) => IsString (StringBuilder a) where
  fromString = StringBuilder . tell

buildString :: StringBuilder () -> String
buildString (StringBuilder w) = execWriter w

writeContent :: StringBuilder ()
writeContent = do
  "Some text. "
  "Some more text. "
  "This should all compile into a single line "
  "and be output to the screen."

main :: IO ()
main = do
  putStr $ buildString writeContent