packages feed

lambdabot-4.1: scripts/FT/FreeTheorems/Delta.hs

-- Copyright 2006, Sascha Boehme.




-- | This module contains the delta algorithm which transforms a type into a
--   relation.

module FreeTheorems.Delta (
    applyDelta,
    applyDeltaWith,
    Environment,

    -- testing interface
    -- testDelta
) where



import Prelude hiding (lookup)

import FreeTheorems.Preparation
import FreeTheorems.Reduction
import FreeTheorems.TheoremData
import FreeTheorems.Types
import qualified Data.Map as Map (Map, empty, insert, lookup)

-- import FreeTheorems.Test.ArbitraryTypes
import Data.List (nub, delete)
-- import Test.QuickCheck (quickCheck)





-- | Applies the delta algorithm to a type to transform it into a corresponding
--   relation. In the generated relation, subrelations are simplified as much as
--   possible.
--
--   According to the language model used, the correct delta algorithm is
--   chosen. The delta algorithm is essentially a term rewriting, i.e. type
--   terms are transformed in corresponding relations while obeying certain
--   rules of the language subset in use.

applyDelta :: LanguageModel -> Type -> TheoremState Relation
applyDelta model = applyDeltaWith model empty



-- | Applies the delta algorithm to a type to transform it into a corresponding
--   relation. In the generated relation, subrelations are simplified as much as
--   possible.
--
--   According to the language model used, the correct delta algorithm is
--   chosen. The delta algorithm is essentially a term rewriting, i.e. type
--   terms are transformed in corresponding relations while obeying certain
--   rules of the language subset in use.
--
--   This function is a variant of 'applyDelta' where an initial environment can
--   be provided.

applyDeltaWith :: LanguageModel -> Environment -> Type -> TheoremState Relation
applyDeltaWith model env t = do
  rel <- case model of
           BasicModel -> deltaBasic env t
           FixModel   -> deltaFix env t
  return $ reduceRelation rel




-- | Declares the type for environments which are mappings of type variables to
--   relation variables.

type Environment = Map.Map TypeVariable Relation



-- | Creates an empty environment.

empty :: Environment
empty = Map.empty



-- | Updates an environment.

update :: Environment -> TypeVariable -> RelationVariable -> Environment
update env v rv = Map.insert v (RelVar rv) env



-- | Returns the relation variable which is mapped to a type variable.

lookup :: Environment -> TypeVariable -> Relation
lookup env v =
  case Map.lookup v env of
    Just rel -> rel
    -- 'Nothing' can not occur because every types processed in 'delta' is
    -- closed and a 'lookup' may only occur after the corresponding 'update'
    -- (due to the structure of types).





-- | The delta algorithm for the basic model.

deltaBasic :: Environment -> Type -> TheoremState Relation
deltaBasic env t =
  case t of
    TypeBasic b     -> return (RelTerm (TermIns (TermVar (PV "id")) t) (t,t))

    TypeVar v       -> return (lookup env v)

    TypeCon c ts    -> do rels <- mapM (deltaBasic env) ts
                          return (RelLift BasicModel c rels)

    TypeList t1     -> do rel <- deltaBasic env t1
                          return (RelLiftList BasicModel rel)

    TypeUnit        -> return (RelTerm (TermIns (TermVar (PV "id")) t) (t, t))

    TypeTuple ts    -> do rels <- mapM (deltaBasic env) ts
                          return (RelLiftTuple BasicModel rels)

    TypeFun t1 t2   -> do rel1 <- deltaBasic env t1
                          rel2 <- deltaBasic env t2
                          return (RelFun BasicModel rel1 rel2)

    TypeForall v t1 -> do rv  <- newRelationVariable v
                          rel <- deltaBasic (update env v rv) t1
                          return (RelForall BasicModel rv rel)



-- | The delta algorithm for the fix model.

deltaFix :: Environment -> Type -> TheoremState Relation
deltaFix env t =
  case t of
    TypeBasic b     -> return (RelTerm (TermIns (TermVar (PV "id")) t) (t,t))

    TypeVar v       -> return (lookup env v)

    TypeCon c ts    -> do rels <- mapM (deltaFix env) ts
                          return (RelLift FixModel c rels)

    TypeList t1     -> do rel <- deltaFix env t1
                          return (RelLiftList FixModel rel)

    TypeUnit        -> return (RelTerm (TermIns (TermVar (PV "id")) t) (t,t))

    TypeTuple ts    -> do rels <- mapM (deltaFix env) ts
                          return (RelLiftTuple FixModel rels)

    TypeFun t1 t2   -> do rel1 <- deltaFix env t1
                          rel2 <- deltaFix env t2
                          return (RelFun FixModel rel1 rel2)

    TypeForall v t1 -> do rv  <- newRelationVariable v
                          rel <- deltaFix (update env v rv) t1
                          return (RelForall FixModel rv rel)



--------------------------------------------------------------------------------



-- A list of tests for this module.

{-
testDelta = do
  putStr "delta preserves the structure ... "
  quickCheck prop_delta_preserves_structure
  putStr "every relation variable occurs only once ... "
  quickCheck prop_every_rv_only_once
  putStr "every relation variable is bound ... "
  quickCheck prop_every_rv_is_bound
  putStr "relation uses only one model ... "
  quickCheck prop_only_one_model
  putStr "delta reduces the generated relation ... "
  quickCheck prop_delta_reduces_relation
-}



-- Check that delta preserves the structure, i.e. the type is just reflected
-- into a representation by relations.

prop_delta_preserves_structure model t =
   let t' = prepare t
       rel = fst $ execute (PV "t") $ applyDelta model t'
   in  compareTypeAndRelation (rel, t')

compareTypeAndRelation :: (Relation, Type) -> Bool
compareTypeAndRelation pair =
  case pair of
    (RelVar rv, TypeVar v)               -> let R _ v' _ = rv
                                            in  v == v'

    (RelTerm _ _, t')                    -> True

    (RelLift _ c rels, TypeCon c' ts)    -> (c == c')
                                            && and (map compareTR $ zip rels ts)

    (RelLiftList _ rel, TypeList t)      -> compareTR (rel, t)

    (RelLiftTuple _ rels, TypeTuple ts)  -> and (map compareTR $ zip rels ts)

    (RelFun _ rel1 rel2, TypeFun t1 t2)  -> compareTR (rel1, t1) &&
                                            compareTR (rel2, t2)

    (RelForall _ rv rel, TypeForall v t) -> let R _ v' _ = rv
                                            in  v' == v && compareTR (rel, t)

    otherwise      -> False

  where
    compareTR = compareTypeAndRelation



-- Check that every relation variable is bound only once in a quantification,
-- i.e. every relation variable is unique.

prop_every_rv_only_once model t =
  let rel = fst $ execute (PV "t") $ applyDelta model $ prepare t
      rvs = getBoundRelVars rel
  in  rvs == nub rvs

getBoundRelVars rel =
  case rel of
    RelTerm _ _         -> []
    RelVar _            -> []
    RelLift _ _ rels    -> concatMap getBoundRelVars rels
    RelLiftList _ rel'  -> getBoundRelVars rel'
    RelLiftTuple _ rels -> concatMap getBoundRelVars rels
    RelFun _ rel1 rel2  -> getBoundRelVars rel1 ++ getBoundRelVars rel2
    RelForall _ rv rel' -> rv : (getBoundRelVars rel')



-- Check that all relation variables are quantified.

prop_every_rv_is_bound model t =
  let rel = fst $ execute (PV "t") $ applyDelta model $ prepare t
  in  checkRelVars rel == []

checkRelVars rel =
  case rel of
    RelTerm _ _         -> []
    RelVar rv           -> [rv]
    RelLift _ _ rels    -> nub $ concatMap checkRelVars rels
    RelLiftList _ rel'  -> nub $ checkRelVars rel'
    RelLiftTuple _ rels -> nub $ concatMap checkRelVars rels
    RelFun _ rel1 rel2  -> nub $ checkRelVars rel1 ++ checkRelVars rel2
    RelForall _ rv rel' -> delete rv (checkRelVars rel')



-- Check that delta creates relations having only one model.

prop_only_one_model model t =
  let rel = fst $ execute (PV "t") $ applyDelta model $ prepare t
  in  usesModel model rel

usesModel model rel =
  case rel of
    RelTerm _ _         -> True
    RelVar rv           -> True
    RelLift m _ rels    -> m == model && and (map (usesModel model) rels)
    RelLiftList m rel'  -> m == model && usesModel model rel'
    RelLiftTuple m rels -> m == model && and (map (usesModel model) rels)
    RelFun m rel1 rel2  -> m == model && usesModel model rel1
                                      && usesModel model rel2
    RelForall m rv rel' -> m == model && usesModel model rel'



-- Check that delta simplifies its generated relations.

prop_delta_reduces_relation model t =
   let rel = fst $ execute (PV "t") $ applyDelta model $ prepare t
   in  rel == reduceRelation rel