鑑於您希望能夠進行分組,並且您還需要能夠確保某些行不會被解除, 爲什麼我們不使用庫設計器對數據類型中的語義進行編碼這一事實, 而不是在代碼中。這個神話般的決定使其顯着可重新設計。
Doc
數據類型使用構造函數Line :: Bool -> Doc
編碼換行符。 布爾表示刪除線條時是否省略空格。 (行縮進時,他們在那裏。) 讓我們更換布爾:
data LineBehaviour = OmitSpace | AddSpace | Keep
data Doc = ...
...
Line !LineBehaviour -- not Bool any more
關於語義作爲數據設計美麗的事情是,如果我們更換 這個Bool
數據與LineBehaviour
數據,即沒有使用它,但 把它傳給了功能不變不需要編輯。看看Bool與012xx什麼時候發生變化的函數 - 我們將通過更改舊語義所在的數據類型來完全重寫代碼 中需要更改以支持新語義的部分。在我們完成所有 更改之前,程序將不會編譯,而我們不需要觸及不依賴換行符語義的 的一行代碼。萬歲!
例如,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
就是這樣:我無法找到任何東西來改變!
美麗。謝謝。當我需要這樣一個更大/更重要的項目時,我會看看這裏。 –