我正在为一个简单的空白区域敏感语言编写一台漂亮的打印机。
我比Leijen漂亮的打印机库更喜欢Wadler库,但Leijen库在我的域中有一个问题:我插入的任何换行符都可能会被group
构造覆盖,这可能会压缩任何一行,可能会改变输出的语义。
我认为我不能在wl-pprint中实现不可分组的行(尽管我喜欢错误)。
看一下wl-pprint-extras包,我不认为即使暴露的内部接口也允许我创建一条不会被group
压扁的行。
我是否只能依赖于我从不使用group
的事实,或者我有更好的选择?
答案 0 :(得分:1)
你确实需要避免group
,是的。该库旨在根据您指定的输出宽度促进包装或不包装。
根据您正在实施的语言的语法,您还应该谨慎使用softline
和softbreak
以及使用它们的</>
和<//>
运算符。我无法确定您无法使用<$>
和<$$>
。
sep
,fillSep
,cat
和fillCat
都直接或间接使用group
(并且具有您想要的不确定语义/宽度相关的换行符避免)。但是,鉴于你的目的,我认为你不需要它们:
使用vsep
或hsep
代替sep
或fillSep
。
使用hcat
或vcat
代替cat
或fillCat
。
您可以使用
这样的行import Text.PrettyPrint.Leijen hiding (group,softline,softbreak,
(</>),(<//>),
sep,fillSep,cat,fillCat)
确保不要调用这些函数。
我想不出一种方法可以确保你使用的函数不会在某个地方调用组,但我认为这些是要避免的。
答案 1 :(得分:1)
鉴于您希望能够分组,并且您还需要能够确保某些行未被插入, 为什么我们不使用库设计者在数据类型中编码语义的事实, 而不是代码。这个神话般的决定使其显着重新可操作。
Doc
数据类型使用构造函数Line :: Bool -> Doc
对换行符进行编码。
Bool表示在删除行时是否省略空格。 (当它们在那里时行缩进。)
让我们取代Bool:
data LineBehaviour = OmitSpace | AddSpace | Keep
data Doc = ...
...
Line !LineBehaviour -- not Bool any more
关于语义 - 数据设计的美妙之处在于,如果我们更换
这个带有Bool
数据的LineBehaviour
数据,但没有使用它的函数
传递它不变,不需要编辑。看看内部的功能
Bool打破了变化 - 我们将完全重写代码的各个部分
需要通过更改数据类型来改变以支持新语义
旧语义存在。在我们完成所有工作之前,程序不会编译
我们应该改变,而我们不需要触及那些没有的代码
依赖换行语义。万岁!
例如,renderPretty
使用Line
构造函数,但在模式Line _
中,
所以我们可以单独留下。
首先,我们需要将Line True
替换为Line OmitSpace
,将Line False
替换为Line AddSpace
,
line = Line AddSpace
linebreak = Line OmitSpace
但也许我们应该添加自己的
hardline :: Doc
hardline = Line Keep
我们也许可以使用使用它的二元运算符
infixr 5 <->
(<->) :: Doc -> Doc -> Doc
x <-> y = x <> hardline <> y
和垂直分离器的等价,我不能想到比非常垂直的分隔符更好的名称:
vvsep,vvcat :: [Doc] -> Doc
vvsep = fold (<->)
vvcat = fold (<->)
实际删除行发生在group
函数中。一切都可以保持不变,除了:
flatten (Line break) = if break then Empty else Text 1 " "
应改为
flatten (Line OmitSpace) = Empty
flatten (Line AddSpace) = Text 1 " "
flatten (Line Keep) = Line Keep
就是这样:我找不到任何可以改变的东西!