我正在为一种简单的空白敏感语言编写一个 pretty-print 。
与 Wadler 库相比,我更喜欢 Leijen pretty-print 库,但是 Leijen 库在我的域中存在一个问题:我插入的任何换行符都可能被 group
构造覆盖,这可能会压缩任何行,这可能会改变输出的语义。
我认为我无法在 wl-pprint 中实现不可分组的行(尽管我很想错)。
稍微看一下 wl-pprint-extras 包,我认为即使是暴露的内部接口(interface)也不允许我创建一条不会被 group
压扁的线。
我是否必须依赖于我从不使用group
的事实,或者我有更好的选择吗?
最佳答案
鉴于您希望能够进行分组,并且还需要能够确保某些行不会被取消插入, 为什么我们不利用库设计者在数据类型中编码语义的事实, 而不是在代码中。这个绝妙的决定使其具有出色的可重新设计性。
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
就是这样:我找不到其他可以更改的内容!
关于haskell - 使用 wl-pprint 不可分组的换行符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12789845/