Haskell嵌套元组格式化:让渲染优先断开外层组softlines
如何让wl-pprint-annotated优先断开外层组的softlines来渲染嵌套元组?
我在用Haskell的wl-pprint-annotated库打印AST时遇到了嵌套元组的排版问题:渲染器总是优先断开内层组的softlines,而我希望外层元组先决定是否换行,内层元组跟着外层的排版策略走。
示例对比
以元组((1234, 5678), (abcd, efgh))为例:
期望输出:
// line width: 10 ( ( 1234, 5678 ), ( abcd, efgh ) ) // line width: 16 ( (1234, 5678), (abcd, efgh) ) // line width: 32 ((1234, 5678), (abcd, efgh))
实际输出:
// line width: 10 ((1234, 5678), (abcd, efgh)) // line width: 16 ((1234, 5678), ( abcd, efgh)) // line width: 32 ((1234, 5678), (abcd, efgh))
原代码
module Main where import qualified Prelude import Prelude hiding((<>)) import Text.PrettyPrint.Annotated.WL main :: IO () main = do putStrLn $ pp 10 putStrLn $ pp 16 putStrLn $ pp 32 pp w = "// line width: " ++ show w ++ "\n" ++ display (renderPretty 1.0 w doc) ++ "\n" doc = pair (pair (text "1234") (text "5678")) (pair (text "abcd") (text "efgh")) pair x y = group (nest 2 (lparen <//> x <> comma </> y) <//> rparen)
解决方案
问题出在每个pair都被包装成独立的group,渲染器会从内到外评估每个组的换行可能性,优先尝试内层不换行,失败就断开内层。要让外层优先换行,我们需要调整组的结构,让外层的排版决策主导内层的行为:
修改pair的定义,用flatAlt指定紧凑排版和换行排版的两种分支,让外层的group主导整个元组的排版逻辑:
module Main where import qualified Prelude import Prelude hiding((<>)) import Text.PrettyPrint.Annotated.WL main :: IO () main = do putStrLn $ pp 10 putStrLn $ pp 16 putStrLn $ pp 32 pp w = "// line width: " ++ show w ++ "\n" ++ display (renderPretty 1.0 w doc) ++ "\n" doc = pair (pair (text "1234") (text "5678")) (pair (text "abcd") (text "efgh")) pair x y = group $ (lparen <+> x <> comma <+> y <+> rparen) `flatAlt` (lparen </> nest 2 (x <> comma </> y) </> rparen)
原理说明
flatAlt a b的作用是:如果当前上下文允许紧凑排版(即a能在剩余宽度内放下),就使用a;否则切换到换行排版的b。- 修改后的
pair不再给内层元组单独设置group,而是让外层的group统一控制整个元组的排版。当外层需要换行时,内层元素才会跟着展开换行;外层能紧凑排版时,内层也保持紧凑。 - 这种结构直接实现了“外层优先断开softlines”的需求,完全匹配期望的输出格式。
内容的提问来源于stack exchange,提问作者Random Citizen
相关产品推荐
相关产品推荐

