lol-calculus-1.20160822: Language/LOL/Calculus/Form.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Calculus.Form where
import Control.Arrow
import Control.Monad
import Data.Bool
import Data.Either (Either(..))
import Data.Eq (Eq(..))
import Data.Foldable (Foldable(..))
import Data.Function (($), (.), id, const, flip, on)
import Data.Functor ((<$>))
import Data.List ((++))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Monoid ()
import Data.Ord (Ord(..))
import Data.Text.Buildable (Buildable(..))
import Data.Traversable (Traversable(..))
import Data.Typeable as Typeable
import Prelude (error)
import Text.Show (Show(..))
import Language.LOL.Calculus.Abstraction
import Language.LOL.Calculus.Term
-- * Type 'Form'
-- | Return a 'Form' from a given 'Term'.
form :: (Eq var, Ord var, Variable var, Typeable var) => Context var -> Term var -> Form var
form ctx te = Form te (normalize ctx te)
-- | Reduce given 'Term' (or 'Type') to /normal form/ (NF)
-- by recursively performing /β-reduction/ and /η-reduction/.
--
-- Note that any well-typed 'Term' (i.e. for which 'type_of' returns a 'Type')
-- is /strongly normalizing/ (i.e. 'normalize' always returns,
-- and its returned 'Term' is unique up to /α-equivalence/).
normalize :: (Eq var, Ord var, Variable var, Typeable var) => Context var -> Term var -> Term var
normalize = go []
where
{-
go_debug :: (Eq var, Ord var, Variable var, Typeable var)
=> [Term var]
-> Context var
-> Term var
-> Term var
go_debug args ctx te =
go args ctx $
trace ("normalize: "
++ "\n te = " ++ show te
++ "\n args = " ++ show args
) $ te
-}
go :: (Eq var, Ord var, Variable var, Typeable var)
=> [Term var]
-> Context var
-> Term var
-> Term var
go args ctx (TeTy_Var ((form_normal <$>) . context_item_term <=< context_lookup ctx -> Just te))
-- NOTE: Replace variable mapped by the context
= case args of
[] -> te -- NOTE: no need to normalize again
_ -> go args ctx te
go args ctx (TeTy_App f x)
-- NOTE: Normalize and collect applied arguments,
-- this to normalize an argument only once for all patterns.
= go (go [] ctx x : args) ctx f
go [] ctx (Term_Abst x f_in f)
-- NOTE: η-reduce: Term_Abst _ (TeTy_App f (TeTy_Var (Var_Bound _)) ==> f
= (Term_Abst x (go [] ctx f_in) ||| id)
(abst_eta_reduce ctx f)
go (x:args) ctx (Term_Abst _ _ f)
-- NOTE: β-reduce: TeTy_App (Term_Abst _ _ f) x ==> f x
= go args ctx (const x `unabstract` f)
go args ctx (Type_Abst x f_in f_out)
-- NOTE: Recurse in Type_Abst fields
= term_apps (Type_Abst x (go [] ctx f_in) (go_scope ctx f_out)) args
go args ctx (TeTy_Axiom (flip (axiom_normalize ctx) args -> Just (r_te, r_args)))
-- NOTE: Normalize axiom
= go r_args ctx r_te
go args _ctx te
-- NOTE: Reapply remaining arguments, normalized
= term_apps te args
abst_eta_reduce
:: (Eq var, Eq bound, Ord var, Ord bound, Variable var, Variable bound, Typeable var, Typeable bound)
=> Context var
-> Abstraction bound Term var
-> Either (Abstraction bound Term var) -- could not η-reduce
(Term var) -- could η-reduce
abst_eta_reduce ctx =
(\abst_body ->
let new_ctx = context_push_nothing ctx in
case go [] new_ctx abst_body of
te@(TeTy_App t (TeTy_Var (Var_Bound _{-Term_Abst's variable-}))) ->
traverse (\var ->
case var of
Var_Free v -> Right v
-- NOTE: decrement the DeBruijn indexing by one
-- to reflect the removal of the Term_Abst.
Var_Bound _ -> Left $ abstract_generalize te
-- NOTE: cannot η-reduce because Term_Abst's variable
-- is also used within t.
) t
te -> Left $ abstract_generalize te
) .
abstract_normalize
go_scope
:: (Eq var, Eq bound, Ord var, Ord bound, Variable var, Variable bound, Typeable var, Typeable bound)
=> Context var
-> Abstraction bound Term var
-> Abstraction bound Term var
go_scope ctx =
abstract_generalize .
go [] (context_push_nothing ctx) .
abstract_normalize
-- | Reduce given 'Term' to /Weak Head Normal Form/ (WHNF).
--
-- __Ressources:__
--
-- * https://wiki.haskell.org/Weak_head_normal_form
whnf :: (Ord var, Variable var, Typeable var) => Context var -> Term var -> Term var
whnf = go []
where
go :: (Ord var, Variable var, Typeable var) => [Term var] -> Context var -> Term var -> Term var
go args ctx (TeTy_Var ((form_given <$>) .
context_item_term <=< context_lookup ctx
-> Just te))
-- NOTE: Replace any variable mapped by the context
= go args ctx te
go args ctx (TeTy_App f a)
-- NOTE: Collect applied arguments
= go (a:args) ctx f
go (x:args) ctx (Term_Abst _ _ f)
-- NOTE: β-reduce: TeTy_App (Term_Abst _ _ f) x ==> f x
= go args ctx (const x `unabstract` f)
go args ctx (TeTy_Axiom (flip (axiom_normalize ctx) args -> Just (r_te, r_args)))
-- NOTE: Normalize axiom
-- TODO: maybe introduce an 'axiom_whnf' instead of 'axiom_normalize'
= go r_args ctx r_te
go args _ctx te
-- NOTE: Reapply remaining arguments, normalized
= term_apps te args
-- | Apply multiple 'Term's to a 'Term',
-- useful to reconstruct a 'Term' while normalizing (see 'normalize' or 'whnf').
term_apps :: Term var -> [Term var] -> Term var
term_apps = foldl TeTy_App
-- | /α-equivalence relation/, synonym of @(==)@.
--
-- Return 'True' iif. both given 'Term's are the same,
-- up to renaming the /bound variables/ it contains (see instance 'Eq' of 'Suggest').
alpha_equiv :: (Eq var, Show var) => Term var -> Term var -> Bool
alpha_equiv = (==)
-- | /αβη-equivalence relation/.
equiv :: (Eq var, Ord var, Variable var, Typeable var) => Context var -> Term var -> Term var -> Bool
equiv ctx = (==) `on` normalize ctx
-- * Type 'Context'
context :: Context Var_Name
context = Context [] (const Nothing) id id
context_apply :: Context var -> Term var -> Term var
context_apply ctx te =
te >>= \var ->
fromMaybe (TeTy_Var var) $ form_normal <$>
(context_item_term =<< context_lookup ctx var)
context_push_type
:: (Eq var, Ord var, Variable var, Typeable var)
=> Context var
-> bound
-> Type var
-> Context (Var bound var)
context_push_type
ctx@(Context keys lookup var_lift var_unlift)
bound ty =
Context
{ context_vars =
Var_Bound bound :
Var_Free `fmap` keys
, context_lookup = \var ->
(Var_Free `fmap`) `fmap`
case var of
Var_Bound _ ->
Just Context_Item
{ context_item_term = Nothing
, context_item_type = form ctx ty
}
Var_Free v -> lookup v
, context_lift = Var_Free . var_lift
, context_unlift = \var ->
case var of
Var_Bound _ -> error "context_push_type: context_unlift"
Var_Free v -> var_unlift v
}
context_push_nothing
:: (Show bound, Buildable (Context var), Typeable var, Typeable bound)
=> Context var
-> Context (Var bound var)
context_push_nothing
(Context keys lookup var_lift var_unlift) =
Context
{ context_vars = Var_Free `fmap` keys
, context_lookup = \var ->
(Var_Free `fmap`) `fmap`
case var of
Var_Bound _ -> Nothing
Var_Free v -> lookup v
, context_lift = Var_Free . var_lift
, context_unlift = \var ->
case var of
-- DEBUG: Var_Bound (cast -> Just (Suggest b)) -> b
-- DEBUG: Var_Bound (cast -> Just b) -> b
Var_Bound b -> error $ "context_push_nothing: context_unlift: " ++ show b
Var_Free v -> var_unlift v
}
context_from_env :: Env -> Context Var_Name
context_from_env env =
Context
{ context_vars = Map.keys env
, context_lookup = \var ->
(\item -> Context_Item
{ context_item_term = Just $ env_item_term item
, context_item_type = env_item_type item
}) <$> env_lookup env var
, context_lift = id
, context_unlift = id
}
context_relift :: forall from_var to_var.
( Typeable from_var
, Typeable to_var )
=> Context from_var
-> Type from_var
-> Context to_var
-> Type to_var
context_relift from_ctx ty to_ctx =
( context_lift to_ctx
. context_unlift from_ctx
) <$> ty
{-
context_from_list :: Eq var => [(var, Type var)] -> Context var
context_from_list l =
Context
{ context_vars = fst <$> l
, context_lookup_type = \var -> List.lookup var l
}
-- | Return /free term variables/ of an 'Abstraction'-ed 'Term'.
ftv
:: Abstraction bound (Term) var
-> Term (Var bound var)
ftv = abstract_normalize
-}
-- * Type 'Env'
type Env
= Map Var_Name Env_Item
data Env_Item
= Env_Item
{ env_item_term :: Form Var_Name
, env_item_type :: Form Var_Name
}
env_item
:: Context Var_Name
-> Term Var_Name
-> Type Var_Name
-> Env_Item
env_item ctx te ty =
Env_Item
{ env_item_term = form ctx te
, env_item_type = form ctx ty
}
env_lookup :: Env -> Var_Name -> Maybe Env_Item
env_lookup env var = Map.lookup var env
env_insert :: Var_Name -> Term Var_Name -> Type Var_Name -> Env -> Env
env_insert var te ty env =
let ctx = context_from_env env in
Map.insert var (env_item ctx te ty) env