Da "fold" nicht leistungsfähig genug ist, um einen hübschen Baumdrucker mit Einrückung zu schreiben, was ist ein Kombinator höherer Ordnung?

Zum Beispiel den folgenden Baumdatentyp:

data Tree a = Node [Tree a] | Leaf a deriving Show
type Sexp = Tree String

Wie drücke ich eine "hübsche" Funktion mit einem Kombinator höherer Ordnung aus, der den Baum mit der richtigen Einrückung druckt? Beispielsweise

sexp = 
    Node [
        Leaf "aaa", 
        Leaf "bbb",
        Node [
            Leaf "ccc",
            Leaf "ddd",
            Node [
                Leaf "eee",
                Leaf "fff"],
            Leaf "ggg",
            Leaf "hhh"],
        Leaf "jjj",
        Leaf "kkk"]
pretty = ????
main = print $ pretty sexp

Ich möchte, dass das Ergebnis dieses Programms ist:

(aaa 
   bbb 
   (ccc 
       ddd 
       (eee 
           fff) 
       ggg 
       hhh) 
   jjj 
   kkk) 

Hier ist eine unvollständige Lösung, die eine "Falte" als Kombinator verwendet und die Einrückung nicht implementiert:

fold f g (Node children) = f (map (fold f g) children)
fold f g (Leaf terminal) = g terminal
pretty = fold (\ x -> "(" ++ (foldr1 ((++) . (++ " ")) x) ++ ")") show
main = putStrLn $ pretty sexp

s ist offensichtlich nicht möglich, die gewünschte Funktion mit @ zu schreibefold, da es die Baumstruktur vergisst. Also, was ist ein richtiger Kombinator höherer Ordnung, der generisch genug ist, um mir das Schreiben der gewünschten Funktion zu ermöglichen, aber weniger leistungsfähig als das Schreiben einer direkten rekursiven Funktion?

Antworten auf die Frage(4)

Ihre Antwort auf die Frage