I am using the Prettier Printer implementation from the contrib library in Idris.
When I fold with the |//| operator on a list of Docs, the performance quickly explodes, i.e. the following code doesn't terminate before I lose my patience:
*IdrisFMT\PrettyDocs> :exec toString 0 15 $ fold (|//|) $ map text $ words "this is a long sentence with a lot of words that I can use for testing the performance of the prettier printer implementation. I need a few more words to prove my point, though."
Please note, the above fold is equal to the pre-defined combinator fillCat.
If I instead use the pre-defined cat combinator (= group . vcat), it terminates within a second:
*IdrisFMT\PrettyDocs> :exec toString 0 15 $ cat $ map text $ words "this is a long sentence with a lot of words that I can use for testing the performance of the prettier printer implementation. I need a few more words to prove my point, though."
"this\nis\na\nlong\nsentence\nwith\na\nlot\nof\nwords\nthat\nI\ncan\nuse\nfor\ntesting\nthe\nperformance\nof\nthe\nprettier\nprinter\nimplementation.\nI\nneed\na\nfew\nmore\nwords\nto\nprove\nmy\npoint,\nthough."
The Doc adt version of cat $ map text $ words "this is a long sentence with a lot of words that I can use for testing the performance of the prettier printer implementation. I need a few more words to prove my point, though." can be seen here: https://pastebin.com/4AJWcGnD
I understand that the cat combinator solves a much simpler problem, but I fail to see how fillCat is so complex as to never terminate.
Could this be caused by a mistake in the implementation, or am I simply constructing too complex a document?
EDIT
The definition of the |//| operator is found here:
https://github.com/idris-lang/Idris-dev/blob/master/libs/contrib/Text/PrettyPrint/WL/Combinators.idr#L65-L69
||| The document `(x |//| y)` concatenates document `x` and `y` with
||| a 'softbreak' in between. This effectively puts `x` and `y` either
||| right next to each other or underneath each other.
(|//|) : Doc -> Doc -> Doc
(|//|) = concatDoc softBreak
This may not be an answer but it could give some hints to someone with a better understanding of
prettyprintlibrary. I used the Idris JVM bytecode backend to diagnose this as I am working on the backend and I was curious to see how it behaves on the JVM. Unsurprisingly the behavior was the same: the first snippet just hangs and the second one works but to debug, I was able to use the JVM tool, jvisualvm, to take the thread dump and profile CPU.Here is the thread dump:
CPU profiling result from jvisualvm:
As we can see from the thread dump, this method call
Text.PrettyPrint.WL.Core.render$colonbest$colon0keeps repeating in the stack trace and, from the CPU profiling, that function is the top hot spot. This method corresponds tobestfunction defined withinrenderfunction ofText.PrettyPrint.WL.Coremodule available here. I am not much familiar with this code but it looks like thebestfunction keeps calling itself without terminating the recursion.