free-theorems-0.2: src/FrontendTypeExpressionsTests.hs
module FrontendTypeExpressionsTests (tests) where
import Data.Generics (everything, something, somewhere, mkQ, gcount, mkM)
import Data.Map as Map (elems, keysSet, singleton, insert, fromList)
import Data.Set as Set
( isSubsetOf, union, member, null, intersection, fromList, difference
, empty, size, singleton )
import Tests
import Language.Haskell.FreeTheorems.Syntax
import Language.Haskell.FreeTheorems.Frontend.TypeExpressions
-- | Runs all tests.
tests :: IO ()
tests = do
doTest "freeVariables are in allVariables" prop_freeVariablesAllVariables
doTest "every variable is free or bound" prop_freeVariablesBoundVariables
doTest "allVariables finds at least one variable of a type "
prop_variablesAreFound
doTest "closure binds free variables" prop_closureVariables
doTest "freeVariables . closure == []" prop_closureFreeVariables
doTest "closureFor emptySet == id" prop_closureForEmptySet
doTest "count after closure is correct" prop_closureCount
doTest "createNewTypeVariable creates unique variables"
prop_createNewTypeVariableNotIn
doTest"count after alphaConversion is correct" prop_alphaConversionCount
doTest "alphaConversion keeps freeVariables" prop_alphaConversionFreeVariables
doTest "alphaConversion substitutes a variable" prop_alphaConversionVariable
doTest "substituteVariable replaces free variable"
prop_substituteTypeVariableReplacesFreeVariables
doTest "substituteVariable keeps free variables"
prop_substituteTypeVariableKeepsFreeVariables
doTest "type is contained after substituteTypeVariables"
prop_substituteTypeVariableTypeInserted
doTest "count after substituteVariable is correct"
prop_substituteTypeVariableWithCount
------- Test properties -------------------------------------------------------
-- | Property: Free variables of a type expression are a subset of all
-- type variables occurring in a type expression.
prop_freeVariablesAllVariables t =
freeTypeVariables t `Set.isSubsetOf` allTypeVariables t
where types = t :: TypeExpression
-- | Property: Every type variable occurring in a type expression is free or
-- bound.
prop_freeVariablesBoundVariables t = (free `Set.union` bound) == all
where
types = t :: TypeExpression
free = freeTypeVariables t
all = allTypeVariables t
bound = boundVariables t
-- | Property: If a type expression contains at least one variable, it is also
-- found by allTypeVariables.
prop_variablesAreFound t =
maybe True (\v -> v `Set.member` allTypeVariables t) (getVariableFrom t)
where
types = t :: TypeExpression
-- | Property: Given a set of type variables, the closure of a type expression
-- for this set does not contain free type variables in that set.
prop_closureVariables t vs =
Set.null (set `Set.intersection` freeTypeVariables (closureFor set t))
where
types = (t :: TypeExpression, vs :: [TypeVariable])
set = Set.fromList vs
-- | Property: There are no free type variables after closing a type expression
-- for all free type variables.
prop_closureFreeVariables t =
Set.null . freeTypeVariables . closureFor (freeTypeVariables t) $ t
where
types = t :: TypeExpression
-- | Property: The closure for an empty set of type variables does not change a
-- type expression.
prop_closureForEmptySet t = closureFor Set.empty t == t
where
types = t :: TypeExpression
-- | Property: The count of type constructors does not increase by the closure.
prop_closureCount t vs =
countTypeAbs t + Set.size set == countTypeAbs (closureFor set t)
&& countTypeFun t == countTypeFun (closureFor set t)
&& countTypeCon t == countTypeCon (closureFor set t)
where
types = (t :: TypeExpression, vs :: [TypeVariable])
set = Set.fromList vs
-- | Property: A newly created type variable does not occur in the list of
-- forbidden type variables.
prop_createNewTypeVariableNotIn vs =
not (createNewTypeVariableNotIn s `Set.member` s)
where
types = vs :: [TypeVariable]
s = Set.fromList vs
-- | Property: Alpha conversion does not increase or decrease the number of
-- type constructors.
prop_alphaConversionCount t v =
countTypeCon t == countTypeCon (alphaConversion v t)
&& countTypeAbs t == countTypeAbs (alphaConversion v t)
&& countTypeFun t == countTypeFun (alphaConversion v t)
where types = (t :: TypeExpression, v :: TypeVariable)
-- | Property: Alpha conversion keeps free variables free.
prop_alphaConversionFreeVariables t v =
freeTypeVariables t == freeTypeVariables (alphaConversion v t)
where types = (t :: TypeExpression, v :: TypeVariable)
-- | Property: After alpha conversion, the replaced variable does not occur
-- in the type expression (except as free variable) and is not bound anymore.
prop_alphaConversionVariable t v =
(v `Set.member` freeAfterAC || not (v `Set.member` allAfterAC))
&& not (v `Set.member` boundVariables acT)
where
types = (t :: TypeExpression, v :: TypeVariable)
acT = alphaConversion v t
allAfterAC = allTypeVariables acT
freeAfterAC = freeTypeVariables acT
-- | Property: Replacing type variables with (closed) type expressions in a
-- type expression removes the free variables from the latter type
-- expression (as long as they are not free in any type expression which is
-- inserted).
prop_substituteTypeVariableReplacesFreeVariables (v1,v2) (t1,t2) t =
(not (v1 `Set.member` vs)
|| v1 `Set.member` freeTypeVariables (fooCon (Map.elems m))
|| not (v1 `Set.member` freeTypeVariables rt))
&&
(not (v2 `Set.member` vs)
|| v2 `Set.member` freeTypeVariables (fooCon (Map.elems m))
|| not (v2 `Set.member` freeTypeVariables rt))
where
types = (v1 :: TypeVariable, v2 :: TypeVariable,
t1 :: TypeExpression, t2 :: TypeExpression,
t :: TypeExpression)
m = Map.fromList [(v1,t1), (v2,t2)]
vs = Map.keysSet m
rt = substituteTypeVariables m t
fooCon = TypeCon (Con $ Ident "Foo")
-- | Property: Replacing a type variable in a type expression keeps the other
-- free variables free.
prop_substituteTypeVariableKeepsFreeVariables (v1,v2) (t1,t2) t i =
(freeT `Set.difference` vs) `Set.isSubsetOf` freeR
where
types = (v1 :: TypeVariable, v2 :: TypeVariable,
t1 :: TypeExpression, t2 :: TypeExpression,
t :: TypeExpression, i :: Int)
m1 = Map.singleton v1 t1
m = if i `mod` 2 == 0 then m1 else Map.insert v2 t2 m1
vs = Map.keysSet m
rt = substituteTypeVariables m t
freeT = freeTypeVariables t
freeR = freeTypeVariables rt
-- | Property: If a type variable which should be substituted by a type
-- expression is free, then the type expression must occur in the result.
prop_substituteTypeVariableTypeInserted (v1,v2) (t1,t2) t =
(not (v1 `Set.member` freeTypeVariables t) || t1 `occursIn` rt)
&&
(not (v2 `Set.member` freeTypeVariables t) || t2 `occursIn` rt)
where
types = (v1 :: TypeVariable, v2 :: TypeVariable,
t1 :: TypeExpression, t2 :: TypeExpression,
t :: TypeExpression)
m = Map.fromList [(v1,t1), (v2,t2)]
vs = Map.keysSet m
rt = substituteTypeVariables m t
-- | Property: Replacing a type variable increases only the number of type
-- constructors.
prop_substituteTypeVariableWithCount (v1,v2) (t1,t2) t i =
countTypeFun rt >= countTypeFun t
&& countTypeAbs rt >= countTypeAbs t
&& countTypeCon rt >= countTypeCon t
where
types = (v1 :: TypeVariable, v2 :: TypeVariable,
t1 :: TypeExpression, t2 :: TypeExpression,
t :: TypeExpression, i :: Int)
m1 = Map.singleton v1 t1
m = if i `mod` 2 == 0 then m1 else Map.insert v2 t2 m1
vs = Map.keysSet m
rt = substituteTypeVariables m t
-- Test helper functions ------------------------------------------------------
-- | Returns a set of all bound variables of a type expression.
boundVariables t = everything Set.union (Set.empty `mkQ` select) t
where
select t = case t of
TypeAbs v _ _ -> Set.singleton v
otherwise -> Set.empty
-- | Returns the first variable found in a type expression.
getVariableFrom t = something (Nothing `mkQ` select) t
where
select t = case t of
TypeVar v -> Just v
TypeAbs v _ _ -> Just v
otherwise -> Nothing
-- | Counts the number of user-defined type constructors.
countTypeCon t = gcount (False `mkQ` select) t
where
select t = case t of
TypeCon _ _ -> True
otherwise -> False
-- | Counts the number of function type constructors.
countTypeFun t = gcount (False `mkQ` select) t
where
select t = case t of
TypeFun _ _ -> True
otherwise -> False
-- | Counts the number of type abstraction constructors.
countTypeAbs t = gcount (False `mkQ` select) t
where
select t = case t of
TypeAbs _ _ _ -> True
otherwise -> False
-- | Returns True if t1 occurs in t2.
t1 `occursIn` t2 = case somewhere (mkM $ findT t2) t1 of
Nothing -> False
Just _ -> True
where
findT t1 t2 = if (t1 == t2) then Just t1 else Nothing