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)))