I'm trying to pretty print a Lambda Expression of the following type
data LamExpr = LamApp LamExpr LamExpr | LamAbs Int LamExpr | LamVar Int deriving (Show, Eq)
I think I made some logic errors with my pattern matching, it's bracketing things incorrectly and I'm unsure why. I've tried adding more cases but I think that overcomplicates things.
printLambda :: LamExpr -> String
printLambda (LamAbs x f) = printf "\\x%d -> %s" x (printLambda f)
printLambda (LamVar x) = printf "x%d" x
printLambda (LamApp (LamAbs x f) (LamApp g h)) = printf "(%s) (%s)" (printLambda (LamAbs x f)) (printLambda (LamApp g h))
printLambda (LamApp m (LamApp f g)) = printf "%s (%s)" (printLambda m) (printLambda (LamApp f g))
printLambda (LamApp (LamApp f g) m) = printf "(%s) %s" (printLambda (LamApp f g)) (printLambda m)
printLambda (LamApp m n) = printf "%s %s" (printLambda m) (printLambda n)
Example case:
printLambda (LamApp (LamAbs 1 (LamVar 1)) (LamAbs 1 (LamVar 1))) == "(\\\\x1 -> x1) \\\\x1 -> x1"
Instead I get this:
"\\\\x1 -> x1 \\\\x1 -> x1"
The data structure in the call to printLambda
is LamApp LamAbs(...) LamAbs(...)
. Let's see why the patterns in the function don't match that:
LamApp (LamAbs xf) (LamApp gh)
doesn't match because you have LamAbs
, not LamApp
as the second argument LamApp m (LamApp fg)
doesn't match for the same reason LamApp (LamApp fg) m
doesn't match because you have LamAbs
, not LamApp
as the first argument LamApp mn
matches any LamApp
and outputs two LamApp
s that aren't enclosed in parenthesesThe more common way to do this (note that the Show
class also works like this) is to add an extra argument to printLambda
that tells you the precedence context.
data Prec = Free | Fun | Arg deriving (Eq, Ord)
printLambda :: Prec -> LamExpr -> String
In a Free
context, there is no danger of the term being "torn apart" by an operation of higher precedence, so no parentheses are needed. The top level of the term is printed in a Free
context, and the body of every lambda abstraction is also Free
. The Fun
context appears on the left of an application. Here, any lambda expression must be parenthesized, or else it will "absorb" the argument ( (\\x1 -> x1) x2
becomes \\x1 -> x1 x2
). Applications are fine, though, since they are left associative ( (fx) y
becomes fxy
stays (fx) y
). The Arg
context is the strictest. You can't have a lambda expression, or else it will consume anything on its right ( f (\\x1 -> x1) x2)
becomes f \\x1 -> x1 x2
). You can't have an application, or it will be shredded ( f (gx)
becomes fgx
becomes (fg) x
).
This helper function conditionally adds parentheses (see built in showParen
):
parens :: Bool -> String -> String
parens True s = "(" ++ s ++ ")"
parens False s = s
And this makes everything much easier:
printLambda _ (LamVar i) = "x" ++ show i
printLambda p (LamAbs i b) = parens (p > Free) $ "\\x" ++ show i ++ " -> " ++ printLambda Free b
printLambda p (LamApp f x) = parens (p > Fun) $ printLambda Fun f ++ " " ++ printLambda Arg x
For your example, this produces (\\x1 -> x1) (\\x1 -> x1)
. The output (\\x1 -> x1) \\x1 -> x1
, as asked, is generally considered incorrect. It's not ambiguous, but adding stuff to its right implies adding parentheses in side the term, which is weird. You could implement it with a new Prec
called Block
(the name is because GHC calls this syntax BlockArguments
)
data Prec = Free | Fun | Arg | Block deriving (Eq, Ord)
that replaces Arg
s directly under Free
:
printLambda _ (LamVar i) = "x" ++ show i
printLambda p (LamAbs i b) = parens (p > Free && p /= Block) $ "\\x" ++ show i ++ " -> " ++ printLambda Free b
printLambda p (LamApp f x) = parens (p > Fun) $ printLambda Fun f ++ " " ++ printLambda arg x
where arg = if p > Free then Arg else Block
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.