从do块中的多个字符串中返回单个字符串

时间:2019-01-04 00:33:14

标签: haskell monads

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

class AddableFunction:
    '''
    Function decorator that lets (f+g)(x) = f(x) + g(x).
    '''
    def __init__(self, function):
        self._function = function
    def __call__(self, *args, **kwargs):
        return self._function(*args, **kwargs)
    def __add__(self, other):
        return AddableFunction(lambda *args, **kwargs: self(*args, **kwargs) + other(*args, **kwargs))

@AddableFunction
def f(x):
    return x ** 2

@AddableFunction
def g(x):
    return x ** 3

print((f + g)(1)) # 2
print((f + g)(2)) # 12
print((f + g)(3)) # 36

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

到目前为止,我已经实现了以下目标:

-- 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!"
    " "

但是我真的很想摆脱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的效率不及++,因此一旦我学到更多,最终将改变这一状况。对Haskell来说还很陌生。我尝试浏览过HaTeX的源代码,但是在某些部分中,一行中完成了太多步骤,因此想一次构建这些小步骤。

1 个答案:

答案 0 :(得分:2)

您可以使用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