Home > OS >  How can I quickly tell how Haskell parses an expression? Is it possible to print the expression with
How can I quickly tell how Haskell parses an expression? Is it possible to print the expression with

Time:01-12

Sometimes I run into expressions full of different operators and I have no idea how they get parsed. I know that I can ask GHCi information about an operator with :i, but having to look at multiple operators and apply their precedence and associativity to reconstruct it in my mind feels quite hard.

Is there any simpler way to understand it?

Example

For example take this valid Haskell expression:

7 ¤ 2 ⏆ 4 ⏆ 3 ¤ 1 ♂ 3 ♂ 2

And assume that the precedence of those operators is:

  • infixl 6 ¤
  • infixl 7 ⏆
  • infixr 8 ♂
Question

Is it possible to ask GHCi to print it out like this?

(7 ¤ ((2 ⏆ 4) ⏆ 3)) ¤ (1 ♂ (3 ♂ 2))

Are there any other ways to quickly get an idea of how that expression works?

CodePudding user response:

You can define an expression type, use those operators to construct an expression, and pretty-print it.

-- E.hs
module E where

data Exp
  = Leaf String
  | Binop Exp String Exp

pretty :: Exp -> String
pretty (Leaf l) = l
pretty (Binop l o r) = "("    pretty l    " "    o    " "    pretty r    ")"

instance Show Exp where
  show = pretty

type B a = a -> a -> a

binop :: String -> B Exp
binop o l r = Binop l o r

instance Num Exp where
  ( ) = binop " "
  (-) = binop "-"
  (*) = binop "*"
  fromInteger = Leaf . show  -- interprets literals as expressions

infixl 6 ¤
infixl 7 ⏆
infixr 8 ♂

(¤), (⏆), (♂) :: B Exp
(¤) = binop "¤"
(⏆) = binop "⏆"
(♂) = binop "♂"

example :: Exp
example = 7 ¤ 2 ⏆ 4 ⏆ 3 ¤ 1 ♂ 3 ♂ 2
$ ghci E.hs
> 7 ¤ 2 ⏆ 4 ⏆ 3 ¤ 1 ♂ 3 ♂ 2
((7 ¤ ((2 ⏆ 4) ⏆ 3)) ¤ (1 ♂ (3 ♂ 2)))

With the above solution, you have to re-define all operators, looking up and copying their precedence levels manually.

Another way is to use Template Haskell to get the actual AST and pretty-print it.

-- T.hs
{-# LANGUAGE TemplateHaskell #-}
module T where

import Language.Haskell.TH

pretty :: Exp -> String
pretty (LitE l) = pprint l
pretty (InfixE (Just x) (VarE n) (Just y)) = "("    pretty x    " "    nameBase n    " "    pretty y    ")"
pretty e = error ("pretty: unhandled "    show e)

prettify :: Q Exp -> Q Exp
prettify q = [| putStrLn |] `appE` fmap (LitE . StringL . pretty) q

Make sure to enable the TemplateHaskell extension.

$ ghci -XTemplateHaskell E.hs T.hs
> :m E T
> $(prettify [| 7 ¤ 2 ⏆ 4 ⏆ 3 ¤ 1 ♂ 3 ♂ 2 |])
((7 ¤ ((2 ⏆ 4) ⏆ 3)) ¤ (1 ♂ (3 ♂ 2)))
  • Related