Вернуть одну строку из нескольких строк в блоке do - PullRequest
0 голосов
/ 04 января 2019

Я пытаюсь достичь чего-то похожего на то, что запрограммировано в 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, поэтому в конце концов это изменится, когда я узнаю немного больше.Я все еще новичок в Хаскеле.Я пытался просмотреть исходный код HaTeX, но есть части, в которых слишком много шагов выполняется в одной строке, поэтому я хотел построить это небольшими шагами за раз.

1 Ответ

0 голосов
/ 04 января 2019

Вы можете использовать пишущую монаду :

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расширение :

{-# 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
...