简体   繁体   中英

Printing lambda expressions in haskell

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
  • finally, LamApp mn matches any LamApp and outputs two LamApp s that aren't enclosed in parentheses

The 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM