lambda-calculator-3.0.0: src/Language/Lambda/SystemF/Expression.hs
module Language.Lambda.SystemF.Expression
( SystemFExpr(..),
Ty(..),
prettyPrint,
upperLambda
) where
import Data.Monoid
import Prettyprinter
import Prettyprinter.Render.Text (renderStrict)
import RIO
data SystemFExpr name ty
-- | Variable: `x`
= Var name
-- | Function application: `x y`
| App (SystemFExpr name ty) (SystemFExpr name ty)
-- | Lambda abstraction: `\x: X. x`
| Abs name (Ty ty) (SystemFExpr name ty)
-- | Type Abstraction: `\X. body`
| TyAbs ty (SystemFExpr name ty)
-- | Type Application: `x [X]`
| TyApp (SystemFExpr name ty) (Ty ty)
deriving (Eq, Show)
data Ty name
= TyVar name -- ^ Type variable (T)
| TyArrow (Ty name) (Ty name) -- ^ Type arrow (T -> U)
| TyForAll name (Ty name) -- ^ Universal type (forall T. X)
deriving (Eq, Show)
instance (Pretty name, Pretty ty) => Pretty (SystemFExpr name ty) where
pretty (Var name) = pretty name
pretty (App e1 e2) = prettyApp e1 e2
pretty (Abs name ty body) = prettyAbs name ty body
pretty (TyAbs ty body) = prettyTyAbs ty body
pretty (TyApp expr ty) = prettyTyApp expr ty
instance Pretty name => Pretty (Ty name) where
pretty = prettyTy False
prettyPrint :: Pretty pretty => pretty -> Text
prettyPrint expr = renderStrict docStream
where docStream = layoutPretty defaultLayoutOptions (pretty expr)
upperLambda :: Char
upperLambda = 'Λ'
prettyApp
:: (Pretty name, Pretty ty)
=> SystemFExpr name ty
-> SystemFExpr name ty
-> Doc a
prettyApp e1@Abs{} e2@Abs{} = parens (pretty e1) <+> parens (pretty e2)
prettyApp e1@Abs{} e2 = parens (pretty e1) <+> pretty e2
prettyApp e1 e2@Abs{} = pretty e1 <+> parens (pretty e2)
prettyApp e1 e2@App{} = pretty e1 <+> parens (pretty e2)
prettyApp e1 e2 = pretty e1 <+> pretty e2
prettyAbs
:: (Pretty name, Pretty ty)
=> name
-> Ty ty
-> SystemFExpr name ty
-> Doc ann
prettyAbs name ty body
= lambda
<+> hsep (map (uncurry prettyArg) names)
<> dot
<+> pretty body'
where (names, body') = uncurryAbs name ty body
prettyTyAbs :: (Pretty name, Pretty ty) => ty -> SystemFExpr name ty -> Doc ann
prettyTyAbs name body = upperLambda' <+> hsep (map pretty names) <> dot
<+> pretty body'
where (names, body') = uncurryTyAbs name body
prettyTyApp :: (Pretty name, Pretty ty) => SystemFExpr name ty -> Ty ty -> Doc ann
prettyTyApp expr ty = pretty expr <+> brackets (pretty ty)
prettyTy :: Pretty name => Bool -> Ty name -> Doc ann
prettyTy _ (TyVar name) = pretty name
prettyTy compact (TyArrow t1 t2) = prettyTyArrow compact t1 t2
prettyTy compact (TyForAll name ty) = prettyTyForAll compact name ty
prettyTyArrow :: Pretty name => Bool -> Ty name -> Ty name -> Doc ann
prettyTyArrow compact (TyArrow t1 t2) t3
= prettyTyArrow' compact compositeTy $ prettyTy compact t3
where compositeTy = parens $ prettyTyArrow compact t1 t2
prettyTyArrow compact t1 t2
= prettyTyArrow' compact (prettyTy compact t1) (prettyTy compact t2)
prettyTyForAll :: Pretty name => Bool -> name -> Ty name -> Doc ann
prettyTyForAll compact name ty
= "forall"
<+> pretty name <> dot
<+> prettyTy compact ty
lambda :: Doc ann
lambda = pretty 'λ'
prettyArg :: (Pretty name, Pretty ty) => name -> Ty ty -> Doc ann
prettyArg name (TyArrow t1 t2)
= pretty name <> colon <> parens (prettyTyArrow True t1 t2)
prettyArg name ty = pretty name <> colon <> pretty ty
upperLambda' :: Doc ann
upperLambda' = pretty upperLambda
prettyTyArrow' :: Bool -> Doc ann -> Doc ann -> Doc ann
prettyTyArrow' compact doc1 doc2 = doc1 `add'` "->" `add'` doc2
where add'
| compact = (<>)
| otherwise = (<+>)
uncurryAbs :: n -> Ty t -> SystemFExpr n t -> ([(n, Ty t)], SystemFExpr n t)
uncurryAbs name ty = uncurry' [(name, ty)]
where uncurry' ns (Abs n' t' body') = uncurry' ((n', t'):ns) body'
uncurry' ns body' = (reverse ns, body')
uncurryTyAbs :: t -> SystemFExpr n t -> ([t], SystemFExpr n t)
uncurryTyAbs ty = uncurry' [ty]
where uncurry' ts (TyAbs t' body') = uncurry' (t':ts) body'
uncurry' ts body' = (reverse ts, body')