packages feed

lambda-calculator-3.1.0.0: src/Language/Lambda/SystemF/Eval.hs

module Language.Lambda.SystemF.Eval
  ( evalExpr,
    subGlobals,
    betaReduce,
    alphaConvert,
    etaConvert,
    freeVarsOf
  ) where

import Language.Lambda.Shared.Errors
import Language.Lambda.Shared.UniqueSupply (next)
import Language.Lambda.SystemF.Expression
import Language.Lambda.SystemF.State

import Control.Monad.Except (throwError)
import Prettyprinter
import RIO
import qualified RIO.Map as Map

-- | Evaluates an expression
evalExpr
  :: (Pretty name, Ord name)
  => SystemFExpr name
  -> Typecheck name (SystemFExpr name)
evalExpr = evalTopLevel

-- | Evaluates a top-level expression
evalTopLevel
  :: (Pretty name, Ord name)
  => SystemFExpr name
  -> Typecheck name (SystemFExpr name)
evalTopLevel (Let n expr) = Let n <$> (subGlobals expr >>= evalInner)
evalTopLevel expr = subGlobals expr >>= evalInner

-- | Evaluates a non top-level expression. Does NOT support Lets
evalInner
  :: (Pretty name, Ord name)
  => SystemFExpr name
  -> Typecheck name (SystemFExpr name)
evalInner (Abs n ty expr) = Abs n ty <$> evalInner expr
evalInner (App e1 e2) = evalApp e1 e2
evalInner (TyAbs n expr) = TyAbs n <$> evalInner expr
evalInner (TyApp expr ty) = evalTyApp expr ty
evalInner (Let n expr) = throwError . InvalidLet . prettyPrint $ Let n expr
evalInner expr = pure expr

subGlobals :: Ord name => SystemFExpr name -> Typecheck name (SystemFExpr name)
subGlobals expr = getGlobals >>= subGlobals'
  where subGlobals' globals' = case expr of
          Var x -> pure . maybe expr (view _expr) $ globals' Map.!? x
          VarAnn x _ -> pure . maybe expr (view _expr) $ globals' Map.!? x
          App e1 e2 -> App <$> subGlobals e1 <*> subGlobals e2
          Abs name ty expr'
            | Map.member name globals' -> pure expr
            | otherwise -> Abs name ty <$> subGlobals expr'
          _ -> pure expr

evalApp
  :: (Pretty name, Ord name)
  => SystemFExpr name
  -> SystemFExpr name
  -> Typecheck name (SystemFExpr name)
evalApp e1 e2 = do
  e1' <- evalInner e1
  e2' <- evalInner e2

  betaReduce e1' e2'

evalTyApp
  :: (Pretty name, Ord name)
  => SystemFExpr name
  -> Ty name
  -> Typecheck name (SystemFExpr name)
evalTyApp expr ty = case expr of
  TyAbs name inner -> evalInner $ substituteTyInExpr ty name inner
  Abs name (TyForAll tyName ty') inner ->
    Abs name (substituteTy ty tyName ty') <$> evalInner inner
  VarAnn name (TyForAll tyName ty') -> pure $ VarAnn name (substituteTy ty tyName ty')
  _ -> TyApp <$> evalInner expr <*> pure ty

betaReduce
  :: (Ord name, Pretty name)
  => SystemFExpr name
  -> SystemFExpr name
  -> Typecheck name (SystemFExpr name)
betaReduce e1 e2 = case e1 of
  App e1' e2' -> App <$> betaReduce e1' e2' <*> pure e2
  Abs n _ e1' -> do
    converted <- alphaConvert (freeVarsOf e2) e1'
    evalInner $ substitute converted n e2
  Let _ _ -> throwError ImpossibleError
  _ -> pure $ App e1 e2

alphaConvert
  :: (Ord name, Pretty name)
  => [name]
  -> SystemFExpr name
  -> Typecheck name (SystemFExpr name)
alphaConvert freeVars (Abs name ty body) = do
  uniques <- getVarUniques
  nextName <- next freeVars uniques
  alphaConvertAbs name ty body freeVars nextName
alphaConvert _ expr = pure expr

etaConvert :: Ord name => SystemFExpr name -> SystemFExpr name
etaConvert (Abs name ty body) = case body of
  App e1 (Var name')
    | name == name' -> etaConvert e1
    | otherwise -> Abs name ty (App (etaConvert e1) (Var name'))
  body'@Abs{}
    | body' == eta' -> Abs name ty body'
    | otherwise -> etaConvert $ Abs name ty eta'
    where eta' = etaConvert body'
  _ -> Abs name ty $ etaConvert body
etaConvert (App e1 e2) = App (etaConvert e1) (etaConvert e2)
etaConvert expr = expr

substitute
  :: Eq name
  => SystemFExpr name
  -> name
  -> SystemFExpr name
  -> SystemFExpr name
substitute expr forName inExpr
  = case expr of
      (Var n)
        | n == forName -> inExpr
        | otherwise -> expr
      (VarAnn n _)
        | n == forName -> inExpr
        | otherwise -> expr
      (Abs n ty body)
        | n == forName -> expr
        | otherwise -> Abs n ty $ substitute body forName inExpr
      (App e1 e2) -> App (sub e1) (sub e2)
      (TyAbs n body) -> TyAbs n $ substitute body forName inExpr
      (TyApp body ty) -> TyApp (substitute body forName inExpr) ty
      _ -> inExpr
  where sub expr' = substitute expr' forName inExpr

substituteTyInExpr
  :: Eq name
  => Ty name
  -> name
  -> SystemFExpr name
  -> SystemFExpr name
substituteTyInExpr ty forName inExpr
  = case inExpr of
      VarAnn name ty' -> VarAnn name (substituteTy ty forName ty')
      App e1 e2 -> App (sub e1) (sub e2)
      Abs name ty' expr -> Abs name (substituteTy ty forName ty') (sub expr)
      TyAbs name expr -> TyAbs name (sub expr)
      TyApp expr ty' -> TyApp (sub expr) (substituteTy ty forName ty')
      _ -> inExpr
  where sub = substituteTyInExpr ty forName

freeVarsOf
  :: (Ord name, Pretty name)
  => SystemFExpr name
  -> [name]
freeVarsOf (Abs n _ expr) = filter (/=n) . freeVarsOf $ expr
freeVarsOf (App e1 e2) = freeVarsOf e1 ++ freeVarsOf e2
freeVarsOf (Var n) = [n]
freeVarsOf (VarAnn n _) = [n]
freeVarsOf (Let _ expr) = freeVarsOf expr
freeVarsOf (TyAbs _ expr) = freeVarsOf expr
freeVarsOf (TyApp expr _) = freeVarsOf expr

alphaConvertAbs
  :: (Ord name, Pretty name)
  => name
  -> Ty name
  -> SystemFExpr name
  -> [name]
  -> name
  -> Typecheck name (SystemFExpr name)
alphaConvertAbs name ty body freeVars nextName
  | name `elem` freeVars = pure $ Abs nextName ty (substitute body name (Var nextName))
  | otherwise = Abs name ty <$> alphaConvert freeVars body