lambda-calculator-3.1.0.0: src/Language/Lambda/SystemF/Expression.hs
module Language.Lambda.SystemF.Expression
( SystemFExpr(..),
TypedExpr(..),
Ty(..),
_expr,
_ty,
prettyPrint,
substituteTy,
upperLambda
) where
import Data.Monoid
import Prettyprinter
import Prettyprinter.Render.Text (renderStrict)
import RIO
data SystemFExpr name
-- | A global binding: `let x = y`
= Let name (SystemFExpr name)
-- | Variable: `x`
| Var name
-- | Variable annotated with type: `x:T`
| VarAnn name (Ty name)
-- | Function application: `x y`
| App (SystemFExpr name) (SystemFExpr name)
-- | Lambda abstraction: `\x: X. x`
| Abs name (Ty name) (SystemFExpr name)
-- | Type Abstraction: `\X. body`
| TyAbs name (SystemFExpr name)
-- | Type Application: `x [X]`
| TyApp (SystemFExpr name) (Ty name)
deriving (Eq, Show)
data TypedExpr name = TypedExpr
{ teExpr :: SystemFExpr name,
teTy :: Ty name
} 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 (Show)
instance (Pretty name) => Pretty (SystemFExpr name) where
pretty (Var name) = pretty name
pretty (VarAnn name ty) = prettyVarAnn name ty
pretty (App e1 e2) = prettyApp e1 e2
pretty (Abs name ty body) = prettyAbs name ty body
pretty (Let name expr) = prettyLet name expr
pretty (TyAbs ty body) = prettyTyAbs ty body
pretty (TyApp expr ty) = prettyTyApp expr ty
instance Pretty name => Pretty (TypedExpr name) where
pretty expr = pretty (expr ^. _expr) <+> colon <+> pretty (expr ^. _ty)
instance Pretty name => Pretty (Ty name) where
pretty = prettyTy False
instance Eq name => Eq (Ty name) where
(==) = isTyEquivalent
_expr :: Lens' (TypedExpr name) (SystemFExpr name)
_expr = lens teExpr (\res expr -> res { teExpr = expr })
_ty :: Lens' (TypedExpr name) (Ty name)
_ty = lens teTy (\res ty -> res { teTy = ty })
prettyPrint :: Pretty pretty => pretty -> Text
prettyPrint expr = renderStrict docStream
where docStream = layoutPretty defaultLayoutOptions (pretty expr)
substituteTy
:: Eq name
=> Ty name
-> name
-> Ty name
-> Ty name
substituteTy ty forName inTy
= case inTy of
TyVar n
| n == forName -> ty
| otherwise -> inTy
TyArrow t1 t2 -> TyArrow (sub t1) (sub t2)
TyForAll n ty'
| n == forName -> inTy
| otherwise -> TyForAll n (sub ty')
where sub = substituteTy ty forName
upperLambda :: Char
upperLambda = 'Λ'
prettyVarAnn :: Pretty name => name -> Ty name -> Doc a
prettyVarAnn var ty = pretty var <> colon <> prettyTy' ty
where prettyTy' (TyVar _) = prettyTy True ty
prettyTy' _ = parens $ prettyTy True ty
prettyApp
:: Pretty name
=> SystemFExpr name
-> SystemFExpr name
-> 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
=> name
-> Ty name
-> SystemFExpr name
-> Doc ann
prettyAbs name ty body
= lambda
<+> hsep (map (uncurry prettyArg) names)
<> dot
<+> pretty body'
where (names, body') = uncurryAbs name ty body
prettyLet :: Pretty name => name -> SystemFExpr name -> Doc ann
prettyLet name expr = "let" <+> pretty name <+> equals <+> pretty expr
prettyTyAbs :: (Pretty name) => name -> SystemFExpr name -> Doc ann
prettyTyAbs name body = upperLambda' <+> hsep (map pretty names) <> dot
<+> pretty body'
where (names, body') = uncurryTyAbs name body
prettyTyApp :: (Pretty name) => SystemFExpr name -> Ty name -> 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
isTyEquivalent :: Eq name => Ty name -> Ty name -> Bool
isTyEquivalent t1 t2
| t1 `isTySame` t2 = True
| otherwise = case (t1, t2) of
(TyForAll n1 t1', TyForAll n2 t2') -> (n1, t1') `areForAllsEquivalent` (n2, t2')
_ -> False
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
isTySame :: Eq name => Ty name -> Ty name -> Bool
isTySame (TyVar n1) (TyVar n2) = n1 == n2
isTySame (TyArrow t1 t2) (TyArrow t1' t2') = t1 == t1' && t2 == t2'
isTySame (TyForAll n1 t1) (TyForAll n2 t2) = n1 == n2 && t1 == t2
isTySame _ _ = False
areForAllsEquivalent :: Eq name => (name, Ty name) -> (name, Ty name) -> Bool
areForAllsEquivalent (n1, t1) (n2, t2) = t1 == substituteTy (TyVar n1) n2 t2
prettyTyArrow' :: Bool -> Doc ann -> Doc ann -> Doc ann
prettyTyArrow' compact doc1 doc2 = doc1 `add'` "->" `add'` doc2
where add'
| compact = (<>)
| otherwise = (<+>)
uncurryAbs :: n -> Ty n -> SystemFExpr n -> ([(n, Ty n)], SystemFExpr n)
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 :: n -> SystemFExpr n -> ([n], SystemFExpr n)
uncurryTyAbs ty = uncurry' [ty]
where uncurry' ts (TyAbs t' body') = uncurry' (t':ts) body'
uncurry' ts body' = (reverse ts, body')