git.iko.soy/type-level-sets
on master help sign in
10 files changed+502−403
since splitsnapshots
unifiedsplit
README+42−21 TySet.hs−255 changelog.md+5 example-new.hs−18 example.hs+26−13 example2.hs+30 src/Data/Type/Map.hs+261 src/Data/Type/Set.hs+78−65 stack.yaml+1−1 type-level-sets.cabal+59−30
README+42−21
@@ -1,35 +1,56 @@
1−This library provides type-level sets to Haskell, with a value level counterpart, and operations for taking the union, computing
2−subsets, and split sets. Also included is a "mapping" type constructor that pairs symbols with types. This can be used to represent
3−sets of variable-type pairs, which is used in the paper "Embedding effect systems in Haskell" (Dominic Orchard,
1+This library provides type-level sets and finite maps to Haskell, with a value level counterpart, and operations for taking the union, computing
2+subsets/submaps, and splitting sets/maps. This library was originally built based on
3+"Embedding effect systems in Haskell" (Dominic Orchard,
44Tomas Petricek <http://www.cl.cam.ac.uk/~dao29/publ/haskell14-effects.pdf>) to embed effect sets.
55
66The following shows an example:
77
8− {-# LANGUAGE DataKinds, TypeOperators #-}
8+ {-# LANGUAGE DataKinds, TypeOperators, TypeFamilies, MultiParamTypeClasses #-}
9+ import Data.Type.Map
10+
11+ -- Specifies how to combine duplicate key-value pairs for Int values
12+ type instance Combine Int Int = Int
13+ instance Combinable Int Int where
14+ combine x y = x + y
15+
16+ foo :: Map '["x" :-> Int, "z" :-> Bool, "w" :-> Int]
17+ foo = Ext (Var :: (Var "x")) 2
18+ $ Ext (Var :: (Var "z")) True
19+ $ Ext (Var :: (Var "w")) 5
20+ Empty
21+
22+ bar :: Map '["y" :-> Int, "w" :-> Int]
23+ bar = Ext (Var :: (Var "y")) 3
24+ $ Ext (Var :: (Var "w")) 1
25+ $ Empty
26+
27+ -- foobar :: Map '["w" :-> Int, "x" :-> Int, "y" :-> Int, "z" :-> Bool]
28+ foobar = foo `union` bar
29+
30+The 'Map' type for 'foobar' here shows the normalised form (sorted with no duplicates).
31+The type signatures is commented out as it can be infered. Running the example we get:
932
10− import Data.Type.Set
33+ *Main> foobar
34+ {w :-> 6, x :-> 2, y :-> 3, z :-> True}
1135
12− foo :: Set '["x" :-> Int, "z" :-> Int, "w" :-> Int]
13− foo = Ext ((Var :: (Var "x")) :-> 2) $
14− Ext ((Var :: (Var "z")) :-> 4) $
15− Ext ((Var :: (Var "w")) :-> 5) $
16− Empty
36+Thus, we see that values for 'w' are added.
1737
18− bar :: Set '["y" :-> Int, "w" :-> Int]
19− bar = Ext ((Var :: (Var "y")) :-> 3) $
20− Ext ((Var :: (Var "w")) :-> 1) $
21− Empty
38+ import GHC.TypeLits
39+ import Data.Type.Set
40+ type instance Cmp (Natural n) (Natural m) = CmpNat n m
2241
23− -- foobar :: Set '["w" :-> Int, "x" :-> Int, "y" :-> Int, "z" :-> Int]
24− foobar = foo `union` bar
42+ data Natural (a :: Nat) where
43+ Z :: Natural 0
44+ S :: Natural n -> Natural (n + 1)
2545
26−The 'Set' type for 'foobar' here shows the normalised form (sorted with no duplicates).
27−The type signatures is commented out as it can be infered. Running the example we get:
46+ -- foo :: Set '[Natural 0, Natural 1, Natural 3]
47+ foo = asSet $ Ext (S Z) (Ext (S (S (S Z))) (Ext Z Empty))
2848
29− *Main> foobar
30− {(Var :-> 1), (Var :-> 2), (Var :-> 3), (Var :-> 4)}
49+ -- bar :: Set '[Natural 1, Natural 2]
50+ bar = asSet $ Ext (S (S Z)) (Ext (S Z) (Ext (S Z) Empty))
3151
32−Thus, we see that the first value paired with the "w" variable is dropped.
52+ -- foobar :: Set '[Natural 0, Natural 1, Natural 2, Natural 3]
53+ foobar = foo `union` bar
3354
3455
3556
TySet.hsdeleted−255
@@ -1,255 +0,0 @@
1−-- {-# OPTIONS_GHC -package=ghc-7.10.1 #-}
2−{-# LANGUAGE TypeFamilies, DataKinds, PolyKinds, StandaloneDeriving, TypeOperators, FlexibleInstances, ScopedTypeVariables #-}
3−
4−module TySet(plugin, Set, Union) where
5−
6−import TypeRep
7−import Type
8−import Kind
9−import TcEvidence
10−import CoAxiom
11−import Name
12−import OccName
13−import Var
14−import TyCon
15−import BasicTypes
16−
17−-- friends:
18−import Var
19−import VarEnv
20−import VarSet
21−import Name
22−import BasicTypes
23−import TyCon
24−import Class
25−import CoAxiom
26−
27−-- others
28−import PrelNames
29−import Outputable
30−import FastString
31−import Util
32−import DynFlags
33−
34−import TcPluginM ( TcPluginM, tcPluginIO, tcPluginTrace )
35−import TcRnMonad ( TcPlugin(..), TcPluginResult(..)
36− , Ct(..), CtEvidence(..), CtLoc, ctLoc, ctPred
37− , mkNonCanonical, isTouchableTcM, unsafeTcPluginTcM)
38−import Plugins ( CommandLineOption, defaultPlugin, Plugin(..) )
39−
40−import Debug.Trace
41−import Outputable
42−import DynFlags
43−
44−import Control.Monad
45−import Control.Applicative
46−import qualified Data.Typeable as DT
47−import Data.List
48−
49−import Test.QuickCheck
50−import Test.QuickCheck.Property
51−
52−{- Set constructor -}
53−data Set (a :: [k])
54−{- Union operation -}
55−type family Union (a :: [k]) (b :: [k]) :: [k]
56−
57−type family Member (a :: k) (b :: [k]) :: Bool where
58− Member x '[] = False
59− Member x (x ': xs) = True
60− Member x (y ': xs) = Member x xs
61−
62−{- Plugin setup -}
63−plugin :: Plugin
64−plugin = defaultPlugin { tcPlugin = Just . thePlugin }
65−
66−thePlugin :: [CommandLineOption] -> TcPlugin
67−thePlugin opts = TcPlugin
68− { tcPluginInit = pluginInit opts
69− , tcPluginSolve = pluginSolve
70− , tcPluginStop = pluginStop
71− }
72−
73−pluginInit :: [CommandLineOption] -> TcPluginM ()
74−pluginInit _ = return ()
75−
76−pluginSolve :: () -> [Ct] -> [Ct] -> [Ct] -> TcPluginM TcPluginResult
77−pluginSolve () given derived wanted =
78− do -- For debugging, show the constraints
79− tcPluginTrace "SET" $ ppCts wanted
80− tcPluginTrace "SET" $ ppr $ head $ wanted
81− solved <- findSetEquality wanted
82− -- For the sake of debugging
83− tcPluginIO $ putStrLn "Running Set equality plugin"
84− return $ TcPluginOk solved []
85−
86−-- Possible eqations for the solver
87−-- Union a b ~ a => b ~ '[]
88−-- Union a b ~ b => a ~ '[]
89−
90−-- Union (Union a b) c ~ d => Union a (Union b c) ~ d
91−-- Union '[] a ~ b => a ~ b
92−-- Union a '[] ~ b => a ~ b
93−-- Union a a ~ b => a ~ b (can do in a reduce phases that looks through a tree of union terms..)
94−
95−-- Reqires an `ordering'
96−-- Union a b ~ c => Union b a ~ c [for the purpose of normalisation]
97−
98−-- Pretty print constraints
99−ppCts [] = text "(empty)"
100−ppCts cs = vcat (map ppr cs)
101−
102−{- Some debugging Show instances to help me understand what is coming in and out of GHC -}
103−deriving instance Show TyLit
104−deriving instance Show Type
105−instance Show Var where
106− show v = showSDocUnsafe $ ppr v
107−instance Show TyCon where
108− show tycon = showSDocUnsafe $ ppr tycon
109−
110−-- Prediacate one whether a type is = Set '[]
111−isEmptySetType t = do (x, tcs) <- splitTyConApp_maybe t
112− guard $ length tcs == 2
113− guard $ getOccString (tyConName x) == "Set"
114− case tcs of [TyVarTy k, TyConApp con [TyVarTy k']] ->
115− do guard $ k == k'
116− guard $ (getOccString . tyConName $ con) == "[]"
117− _ -> Nothing
118−
119−getNameStr = getOccString . tyConName
120−
121−isUnion :: Type -> Bool
122−isUnion t = case splitTyConApp_maybe t of
123− Just (x, tcs) -> (getOccString . tyConName $ x) == "Union"
124− _ -> False
125−
126−-- splitUnion :: Type -> ([Type], [Type])
127−splitUnion t = do (x, tcs) <- splitTyConApp_maybe t
128− guard $ (getOccString . tyConName $ x) == "Union"
129−
130−splitList t = do (tcon, args) <- splitTyConApp_maybe t
131− case getNameStr tcon of
132− ":" -> do guard $ (length args >= 2)
133− (t1 : (t2 : ts)) <- return args
134− (kind, emptyArgs) <- splitTyConApp_maybe $ t1
135− guard $ emptyArgs == []
136− -- maybe be unnecessary and even restrictive
137− guard $ getNameStr kind == "*"
138− ts' <- splitList $ head ts -- possible bug here
139− return $ (t2 : ts')
140− "[]" -> do guard $ (length args == 1)
141− [t] <- return args
142− (kind, emptyArgs) <- splitTyConApp_maybe $ t
143− -- maybe be unnecessary and even restrictive
144− guard $ getNameStr kind == "*"
145− guard $ emptyArgs == []
146− return $ []
147− _ -> return $ []
148−
149−unionSingle t = do (x, tcs) <- splitTyConApp_maybe t
150− guard $ length tcs == 2
151− guard $ getNameStr x == "Set"
152− return ()
153−
154−{- Experimenting, finds Set '[] ~ Set '[] and returns a refl coercion
155− This was set up before when 'Set' was a type family, but is now redundant
156−-}
157−findSetEquality :: [Ct] -> TcPluginM [(EvTerm, Ct)]
158−findSetEquality [] = return []
159−findSetEquality (ct : xs) = let x = (do (Nominal,t1,t2) <- getEqPredTys_maybe (ctPred ct)
160− isEmptySetType t1
161− isEmptySetType t2
162− return ((EvCoercion $ TcRefl Nominal t1, ct), t1))
163− y = do (Nominal,t1,t2) <- getEqPredTys_maybe (ctPred ct)
164− return t1
165− in
166− case x of
167− Just (ct, tcsA) -> do xs' <- (findSetEquality xs)
168− tcPluginTrace "SET" $ ppr $ tcsA
169− tcPluginTrace "SET" $ text $ show tcsA
170− return $ ct : xs'
171− Nothing -> case y of
172− Just t -> do tcPluginTrace "SET" $ text $ show t
173− return []
174−
175− Nothing -> findSetEquality xs
176−
177−pluginStop :: () -> TcPluginM ()
178−pluginStop _ = return ()
179−
180−data TermTree a = Union (TermTree a) (TermTree a) | Empty | Var String | Data [a] deriving Show
181−
182−equal :: Eq a => TermTree a -> TermTree a -> Bool
183−equal t1 t2 = let (vs1, ds1) = normalisedRep t1
184− (vs2, ds2) = normalisedRep t2
185− in (vs1 == vs2) && (all (flip elem $ ds2) ds1) && (all (flip elem $ ds1) ds2)
186− where
187− normalisedRep :: Eq a => TermTree a -> ([String], [a])
188− normalisedRep t = let (vs, ds) = separate t
189− in (nub . sort $ vs, nub ds)
190−
191−separate :: TermTree a -> ([String], [a])
192−separate Empty = ([], [])
193−separate (Var s) = ([s], [])
194−separate (Data xs) = ([], xs)
195−separate (Union a b) = let (vs1, ds1) = separate a
196− (vs2, ds2) = separate b
197− in (vs1++vs2, ds1++ds2)
198−
199−{-- Unit testing of normaliser and set equality --}
200−testSetTermEquality :: IO ()
201−testSetTermEquality = quickCheck (\((n, m) :: (TermTree Int, TermTree Int)) -> equal n m)
202−
203−instance Arbitrary (TermTree Int, TermTree Int) where
204− arbitrary = sized $ \vars ->
205− sized $ \datums ->
206− do v <- (vector vars)::(Gen [String])
207− dat <- (vector datums)::(Gen [Int])
208− v' <- shuffle v
209− dat' <- shuffle dat
210− g1 <- gen v dat
211− g2 <- gen v' dat'
212−
213− -- Soundness check on generated tree
214− let (v0, dat0) = separate g1
215− (v1, dat1) = separate g2
216− norm :: (Ord a) => [a] -> [a]
217− norm = nub . sort
218−
219− if and [(norm v0) == (norm v), (norm v1) == (norm v),
220− (norm dat) == (norm dat0), (norm dat) == (norm dat1)]
221− then return ()
222− else error $ "Generated trees failed soundness check: " ++ (show g1) ++ " " ++ (show g2)
223− return (g1, g2)
224−
225−-- Arbitrarily permute a union of two generators
226−unionPerm x y = do x' <- x
227− y' <- y
228− elements [Union x' y', Union y' x']
229−
230−-- Generates arbitrary terms from a list of variables and list of Int adtums
231−gen :: [String] -> [Int] -> Gen (TermTree Int)
232−gen [] [] = return $ Empty
233−gen vs ds = do -- Choose between 0 and 1 if 'vs' is empty, or 0-2 if 'vs' has elements
234− choose <- suchThat arbitrary (\x -> x<=(2 - if vs == [] then 1 else 0) && x>=0)
235− case choose::Int of
236− -- Union with Empty
237− 0 -> unionPerm (gen vs ds) (return $ Empty)
238−
239− -- Pick some number of elements (maybe none) and create a data leaf
240− 1 -> do i <- suchThat arbitrary (<= (length ds))
241− unionPerm (gen vs (drop i ds)) (return $ Data (take i ds))
242−
243− -- Case where vs is non-empty, create a variable node, and either remove that
244− -- variable, or keep it around as a possibility again to test idempotency
245− 2 -> oneof [unionPerm (gen (tail vs) ds) (return $ Var (head vs)),
246− unionPerm (gen vs ds) (return $ Var (head vs))]
247− _ -> error "unpossible"
248−
249−
250−
251−
252−
253−
254−
255−
changelog.mdnew file+5
@@ -0,0 +1,5 @@
1+ # v0.8.9.0
2+
3+ - GHC 8.4 and 8.6 support.
4+ - Fixed bug in the delete operation.
5+ - Non-membership predicates
example-new.hsdeleted−18
@@ -1,18 +0,0 @@
1−{-# LANGUAGE DataKinds, KindSignatures, GADTs, TypeOperators, TypeFamilies, PolyKinds #-}
2−{-# OPTIONS_GHC -fplugin=TySet #-}
3−
4−import GHC.TypeLits
5−import TySet
6−
7−data Container (a :: *) where
8− Empty :: Container (Set '[])
9− Ext :: a -> Container (Set s) -> Container (Set (Union '[a] s))
10−
11−data Natural (a :: Nat) where
12− Z :: Natural 0
13− S :: Natural n -> Natural (n + 1)
14−
15−-- Should be typeable with the plugin
16−foo :: Container (Set '[Natural 1, Natural 2, Natural 3])
17−foo = Ext (S $ S Z) (Ext (S Z) (Ext (S $ S $ S Z) Empty))
18−
example.hs+26−13
@@ -1,18 +1,31 @@
1−{-# LANGUAGE DataKinds, TypeOperators #-}
1+{-# LANGUAGE DataKinds, TypeOperators, TypeFamilies, MultiParamTypeClasses #-}
22
3−import Data.Type.Set
3+import GHC.TypeLits
4+import Data.Type.Map
45
5−foo :: Set '["x" :-> Int, "z" :-> Int, "w" :-> Int]
6−foo = Ext ((Var :: (Var "x")) :-> 2) $
7− Ext ((Var :: (Var "z")) :-> 4) $
8− Ext ((Var :: (Var "w")) :-> 5) $
9− Empty
6+-- Specify that key-value pairs on Ints combine to an Int
7+type instance Combine Int Int = Int
8+-- Specify that Int values for matching keys should be added
9+instance Combinable Int Int where
10+ combine x y = x + y
1011
11−bar :: Set '["y" :-> Int, "w" :-> Int]
12−bar = Ext ((Var :: (Var "y")) :-> 3) $
13− Ext ((Var :: (Var "w")) :-> 1) $
14− Empty
12+foo :: Map '["x" :-> Int, "z" :-> Bool, "w" :-> Int]
13+foo = Ext (Var :: (Var "x")) 2
14+ $ Ext (Var :: (Var "z")) True
15+ $ Ext (Var :: (Var "w")) 5
16+ $ Empty
17+
18+foo' :: Map (AsMap '["z" :-> Bool, "x" :-> Int, "w" :-> Int])
19+foo' = asMap foo
20+
21+bar :: Map '["y" :-> Int, "w" :-> Int]
22+bar = Ext (Var :: (Var "y")) 3 $
23+ Ext (Var :: (Var "w")) 1 $
24+ Empty
1525
1626-- GHC can easily infer this type, so an explicit signature not necessary
17−-- foobar :: Set '["w" :-> Int, "x" :-> Int, "y" :-> Integer, "z" :-> Int]
18−foobar = foo `union` bar
27+-- foobar :: Map '["w" :-> Int, "x" :-> Int, "y" :-> Integer, "z" :-> Int]
28+foobar = foo `union` bar
29+
30+foobarToFoo :: Map '["w" :-> Int, "x" :-> Int, "z" :-> Bool]
31+foobarToFoo = submap foobar
example2.hsnew file+30
@@ -0,0 +1,30 @@
1+{-# LANGUAGE DataKinds, TypeOperators, TypeFamilies, GADTs, StandaloneDeriving #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+
4+import GHC.TypeLits
5+import Data.Type.Set
6+
7+type instance Cmp (Natural n) (Natural m) = CmpNat n m
8+
9+data Natural (a :: Nat) where
10+ Z :: Natural 0
11+ S :: Natural n -> Natural (n + 1)
12+
13+deriving instance Show (Natural n)
14+
15+-- foo :: Set '[Natural 0, Natural 1, Natural 3]
16+foo = asSet $ Ext (S Z) (Ext (S (S (S Z))) (Ext Z Empty))
17+
18+-- bar :: Set '[Natural 1, Natural 2]
19+bar = asSet $ Ext (S (S Z)) (Ext (S Z) (Ext (S Z) Empty))
20+
21+-- foobar :: Set '[Natural 0, Natural 1, Natural 2, Natural 3]
22+foobar = foo `union` bar
23+
24+nonMemberTest :: NonMember (Natural 0) as => Set as -> ()
25+nonMemberTest set = ()
26+
27+-- nonMemberTest meep is well typed
28+meep = asSet $ Ext (S Z) (Ext (S (S Z)) Empty)
29+-- nonMemberTest morp is ill typed
30+morp = asSet $ Ext (S Z) (Ext (S (S Z)) (Ext Z Empty))
src/Data/Type/Map.hsnew file+261
@@ -0,0 +1,261 @@
1+{- This module provides type-level finite maps.
2+The implementation is similar to that shown in the paper.
3+ "Embedding effect systems in Haskell" Orchard, Petricek 2014 -}
4+
5+{-# LANGUAGE TypeOperators, PolyKinds, DataKinds, KindSignatures,
6+ TypeFamilies, UndecidableInstances, MultiParamTypeClasses,
7+ FlexibleInstances, GADTs, FlexibleContexts, ScopedTypeVariables,
8+ ConstraintKinds, IncoherentInstances #-}
9+
10+module Data.Type.Map (Mapping(..), Union, Unionable, union, Var(..), Map(..),
11+ Combine, Combinable(..), Cmp,
12+ Nubable, nub,
13+ Lookup, Member, (:\), Split, split,
14+ IsMember, lookp, Updatable, update,
15+ IsMap, AsMap, asMap,
16+ Submap, submap) where
17+
18+import GHC.TypeLits
19+import Data.Type.Bool
20+import Data.Type.Equality
21+import Data.Type.Set (Cmp, Proxy(..), Flag(..), Sort, Filter, (:++))
22+
23+{- Throughout, type variables
24+ 'k' ranges over "keys"
25+ 'v' ranges over "values"
26+ 'kvp' ranges over "key-value-pairs"
27+ 'm', 'n' range over "maps" -}
28+
29+-- Mappings
30+infixr 4 :->
31+{-| A key-value pair -}
32+data Mapping k v = k :-> v
33+
34+{-| Union of two finite maps -}
35+type Union m n = Nub (Sort (m :++ n))
36+
37+{-| Apply 'Combine' to values with matching key (removes duplicate keys) -}
38+type family Nub t where
39+ Nub '[] = '[]
40+ Nub '[kvp] = '[kvp]
41+ Nub ((k :-> v1) ': (k :-> v2) ': m) = Nub ((k :-> Combine v1 v2) ': m)
42+ Nub (kvp1 ': kvp2 ': s) = kvp1 ': Nub (kvp2 ': s)
43+
44+{-| Open type family for combining values in a map (that have the same key) -}
45+type family Combine (a :: v) (b :: v) :: v
46+
47+{-| Delete elements from a map by key -}
48+type family (m :: [Mapping k v]) :\ (c :: k) :: [Mapping k v] where
49+ '[] :\ k = '[]
50+ ((k :-> v) ': m) :\ k = m :\ k
51+ (kvp ': m) :\ k = kvp ': (m :\ k)
52+
53+{-| Type-level lookup of elements from a map -}
54+type family Lookup (m :: [Mapping k v]) (c :: k) :: Maybe v where
55+ Lookup '[] k = Nothing
56+ Lookup ((k :-> v) ': m) k = Just v
57+ Lookup (kvp ': m) k = Lookup m k
58+
59+{-| Membership test as type function -}
60+type family Member (c :: k) (m :: [Mapping k v]) :: Bool where
61+ Member k '[] = False
62+ Member k ((k :-> v) ': m) = True
63+ Member k (kvp ': m) = Member k m
64+
65+-----------------------------------------------------------------
66+-- Value-level map with a type-level representation
67+
68+{-| Pair a symbol (representing a variable) with a type -}
69+data Var (k :: Symbol) = Var
70+
71+instance KnownSymbol k => Show (Var k) where
72+ show = symbolVal
73+
74+{-| A value-level heterogenously-typed Map (with type-level representation in terms of lists) -}
75+data Map (n :: [Mapping Symbol *]) where
76+ Empty :: Map '[]
77+ Ext :: Var k -> v -> Map m -> Map ((k :-> v) ': m)
78+
79+{-| Membership test a type class (predicate) -}
80+class IsMember v t m where
81+ {-| Value-level lookup of elements from a map, via type class predicate -}
82+ lookp :: Var v -> Map m -> t
83+
84+instance {-# OVERLAPS #-} IsMember v t ((v ':-> t) ': m) where
85+ lookp _ (Ext _ x _) = x
86+
87+instance {-# OVERLAPPABLE #-} IsMember v t m => IsMember v t (x ': m) where
88+ lookp v (Ext _ _ m) = lookp v m
89+
90+
91+{-| Updatability as a type class -}
92+class Updatable v t m n where
93+ {-| Update a map with `m` at variable `v` with a value of type `t`
94+ to produce a map of type `n` -}
95+ update :: Map m -> Var v -> t -> Map n
96+
97+instance {-# OVERLAPS #-} Updatable v t ((v ':-> s) ': m) ((v ':-> t) ': m) where
98+ update (Ext v _ m) _ x = Ext v x m
99+
100+instance Updatable v t m n => Updatable v t ((w ':-> y) ': m) ((w ':-> y) ': n) where
101+ update (Ext w y m) v x = Ext w y (update m v x)
102+
103+-- instance Updatable v t '[] '[v ':-> t] where
104+-- update Empty v x = Ext v x Empty
105+
106+instance Updatable v t s ((v ':-> t) ': s) where
107+ update xs v x = Ext v x xs
108+
109+
110+{-| Predicate to check if in normalised map form -}
111+type IsMap s = (s ~ Nub (Sort s))
112+
113+{-| At the type level, normalise the list form to the map form -}
114+type AsMap s = Nub (Sort s)
115+
116+{-| At the value level, noramlise the list form to the map form -}
117+asMap :: (Sortable s, Nubable (Sort s)) => Map s -> Map (AsMap s)
118+asMap x = nub (quicksort x)
119+
120+instance Show (Map '[]) where
121+ show Empty = "{}"
122+
123+instance (KnownSymbol k, Show v, Show' (Map s)) => Show (Map ((k :-> v) ': s)) where
124+ show (Ext k v s) = "{" ++ show k ++ " :-> " ++ show v ++ show' s ++ "}"
125+
126+class Show' t where
127+ show' :: t -> String
128+instance Show' (Map '[]) where
129+ show' Empty = ""
130+instance (KnownSymbol k, Show v, Show' (Map s)) => Show' (Map ((k :-> v) ': s)) where
131+ show' (Ext k v s) = ", " ++ show k ++ " :-> " ++ show v ++ (show' s)
132+
133+instance Eq (Map '[]) where
134+ Empty == Empty = True
135+
136+instance (Eq v, Eq (Map s)) => Eq (Map ((k :-> v) ': s)) where
137+ (Ext Var v m) == (Ext Var v' m') = v == v' && m == m'
138+
139+instance Ord (Map '[]) where
140+ compare Empty Empty = EQ
141+
142+instance (Ord v, Ord (Map s)) => Ord (Map ((k :-> v) ': s)) where
143+ compare (Ext Var v m) (Ext Var v' m') = compare v v' `mappend` compare m m'
144+
145+{-| Union of two finite maps (normalising) -}
146+union :: (Unionable s t) => Map s -> Map t -> Map (Union s t)
147+union s t = nub (quicksort (append s t))
148+
149+type Unionable s t = (Nubable (Sort (s :++ t)), Sortable (s :++ t))
150+
151+{-| Append of two finite maps (non normalising) -}
152+append :: Map s -> Map t -> Map (s :++ t)
153+append Empty x = x
154+append (Ext k v xs) ys = Ext k v (append xs ys)
155+
156+type instance Cmp (k :: Symbol) (k' :: Symbol) = CmpSymbol k k'
157+type instance Cmp (k :-> v) (k' :-> v') = CmpSymbol k k'
158+
159+{-| Value-level quick sort that respects the type-level ordering -}
160+class Sortable xs where
161+ quicksort :: Map xs -> Map (Sort xs)
162+
163+instance Sortable '[] where
164+ quicksort Empty = Empty
165+
166+instance (Sortable (Filter FMin (k :-> v) xs)
167+ , Sortable (Filter FMax (k :-> v) xs)
168+ , FilterV FMin k v xs
169+ , FilterV FMax k v xs) => Sortable ((k :-> v) ': xs) where
170+ quicksort (Ext k v xs) =
171+ quicksort (less k v xs) `append` Ext k v Empty `append` quicksort (more k v xs)
172+ where
173+ less = filterV (Proxy::(Proxy FMin))
174+ more = filterV (Proxy::(Proxy FMax))
175+
176+{- Filter out the elements less-than or greater-than-or-equal to the pivot -}
177+class FilterV (f::Flag) k v xs where
178+ filterV :: Proxy f -> Var k -> v -> Map xs -> Map (Filter f (k :-> v) xs)
179+
180+instance FilterV f k v '[] where
181+ filterV _ k v Empty = Empty
182+
183+instance (Conder (Cmp x (k :-> v) == LT), FilterV FMin k v xs) => FilterV FMin k v (x ': xs) where
184+ filterV f@Proxy k v (Ext k' v' xs) =
185+ cond (Proxy::(Proxy (Cmp x (k :-> v) == LT)))
186+ (Ext k' v' (filterV f k v xs))
187+ (filterV f k v xs)
188+
189+instance
190+ (Conder ((Cmp x (k :-> v) == GT) || (Cmp x (k :-> v) == EQ)), FilterV FMax k v xs)
191+ => FilterV FMax k v (x ': xs) where
192+ filterV f@Proxy k v (Ext k' v' xs) =
193+ cond (Proxy::(Proxy ((Cmp x (k :-> v) == GT) || (Cmp x (k :-> v) == EQ))))
194+ (Ext k' v' (filterV f k v xs))
195+ (filterV f k v xs)
196+
197+class Combinable t t' where
198+ combine :: t -> t' -> Combine t t'
199+
200+class Nubable t where
201+ nub :: Map t -> Map (Nub t)
202+
203+instance Nubable '[] where
204+ nub Empty = Empty
205+
206+instance Nubable '[e] where
207+ nub (Ext k v Empty) = Ext k v Empty
208+
209+instance {-# OVERLAPPABLE #-}
210+ (Nub (e ': f ': s) ~ (e ': Nub (f ': s)),
211+ Nubable (f ': s)) => Nubable (e ': f ': s) where
212+ nub (Ext k v (Ext k' v' s)) = Ext k v (nub (Ext k' v' s))
213+
214+instance {-# OVERLAPS #-}
215+ (Combinable v v', Nubable ((k :-> Combine v v') ': s))
216+ => Nubable ((k :-> v) ': (k :-> v') ': s) where
217+ nub (Ext k v (Ext k' v' s)) = nub (Ext k (combine v v') s)
218+
219+
220+class Conder g where
221+ cond :: Proxy g -> Map s -> Map t -> Map (If g s t)
222+
223+instance Conder True where
224+ cond _ s t = s
225+
226+instance Conder False where
227+ cond _ s t = t
228+
229+
230+{-| Splitting a union of maps, given the maps we want to split it into -}
231+class Split s t st where
232+ -- where st ~ Union s t
233+ split :: Map st -> (Map s, Map t)
234+
235+instance Split '[] '[] '[] where
236+ split Empty = (Empty, Empty)
237+
238+instance {-# OVERLAPPABLE #-} Split s t st => Split (x ': s) (x ': t) (x ': st) where
239+ split (Ext k v st) = let (s, t) = split st
240+ in (Ext k v s, Ext k v t)
241+
242+instance {-# OVERLAPS #-} Split s t st => Split (x ': s) t (x ': st) where
243+ split (Ext k v st) = let (s, t) = split st
244+ in (Ext k v s, t)
245+
246+instance {-# OVERLAPS #-} (Split s t st) => Split s (x ': t) (x ': st) where
247+ split (Ext k v st) = let (s, t) = split st
248+ in (s, Ext k v t)
249+
250+{-| Construct a submap 's' from a supermap 't' -}
251+class Submap s t where
252+ submap :: Map t -> Map s
253+
254+instance Submap '[] '[] where
255+ submap xs = Empty
256+
257+instance {-# OVERLAPPABLE #-} Submap s t => Submap s (x ': t) where
258+ submap (Ext _ _ xs) = submap xs
259+
260+instance {-# OVERLAPS #-} Submap s t => Submap (x ': s) (x ': t) where
261+ submap (Ext k v xs) = Ext k v (submap xs)
src/Data/Type/Set.hs+78−65
@@ -1,27 +1,32 @@
1−{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeOperators, TypeFamilies,
2− MultiParamTypeClasses, FlexibleInstances, PolyKinds, FlexibleContexts,
3− UndecidableInstances, ConstraintKinds, ScopedTypeVariables #-}
1+{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeOperators, TypeFamilies,
2+ MultiParamTypeClasses, FlexibleInstances, PolyKinds,
3+ FlexibleContexts, UndecidableInstances, ConstraintKinds,
4+ ScopedTypeVariables, TypeInType #-}
45
5−module Data.Type.Set (Set(..), Union, Unionable, union, quicksort, append,
6− Sort, Sortable, Append(..), Split(..), Cmp,
6+module Data.Type.Set (Set(..), Union, Unionable, union, quicksort, append,
7+ Sort, Sortable, (:++), Split(..), Cmp, Filter, Flag(..),
78 Nub, Nubable(..), AsSet, asSet, IsSet, Subset(..),
8− (:->)(..), Var(..), Delete(..)) where
9+ Delete(..), Proxy(..), remove, Remove, (:\),
10+ Member(..), NonMember, MemberP(..)) where
911
1012import GHC.TypeLits
1113import Data.Type.Bool
1214import Data.Type.Equality
13−import Data.Proxy
1415
15−{-| Core Set definition, in terms of lists -}
16−data Set (n :: [*]) where
16+data Proxy (p :: k) = Proxy
17+
18+-- Value-level 'Set' representation, essentially a list
19+data Set (n :: [k]) where
20+ {--| Construct an empty set -}
1721 Empty :: Set '[]
22+ {--| Extend a set with an element -}
1823 Ext :: e -> Set s -> Set (e ': s)
1924
2025instance Show (Set '[]) where
2126 show Empty = "{}"
2227
2328instance (Show e, Show' (Set s)) => Show (Set (e ': s)) where
24− show (Ext e s) = "{" ++ show e ++ (show' s) ++ "}"
29+ show (Ext e s) = "{" ++ show e ++ (show' s) ++ "}"
2530
2631class Show' t where
2732 show' :: t -> String
@@ -30,6 +35,10 @@ instance Show' (Set '[]) where
3035instance (Show' (Set s), Show e) => Show' (Set (e ': s)) where
3136 show' (Ext e s) = ", " ++ show e ++ (show' s)
3237
38+instance (Eq e, Eq (Set s)) => Eq (Set (e ': s)) where
39+ (Ext e m) == (Ext e' m') = e == e' && m == m'
40+
41+
3342{-| At the type level, normalise the list form to the set form -}
3443type AsSet s = Nub (Sort s)
3544
@@ -41,54 +50,68 @@ asSet x = nub (quicksort x)
4150type IsSet s = (s ~ Nub (Sort s))
4251
4352{-| Useful properties to be able to refer to someties -}
44−type SetProperties f = (Union f '[] ~ f, Split f '[] f,
45− Union '[] f ~ f, Split '[] f f,
53+type SetProperties f = (Union f '[] ~ f, Split f '[] f,
54+ Union '[] f ~ f, Split '[] f f,
4655 Union f f ~ f, Split f f f,
4756 Unionable f '[], Unionable '[] f)
4857
4958{-- Union --}
5059
5160{-| Union of sets -}
52−type Union s t = Nub (Sort (Append s t))
61+type Union s t = Nub (Sort (s :++ t))
5362
5463union :: (Unionable s t) => Set s -> Set t -> Set (Union s t)
5564union s t = nub (quicksort (append s t))
5665
57−type Unionable s t = (Sortable (Append s t), Nubable (Sort (Append s t)))
66+type Unionable s t = (Sortable (s :++ t), Nubable (Sort (s :++ t)))
5867
5968{-| List append (essentially set disjoint union) -}
60−type family Append (s :: [k]) (t :: [k]) :: [k] where
61− Append '[] t = t
62− Append (x ': xs) ys = x ': (Append xs ys)
69+type family (:++) (x :: [k]) (y :: [k]) :: [k] where
70+ '[] :++ xs = xs
71+ (x ': xs) :++ ys = x ': (xs :++ ys)
6372
64−append :: Set s -> Set t -> Set (Append s t)
73+append :: Set s -> Set t -> Set (s :++ t)
6574append Empty x = x
6675append (Ext e xs) ys = Ext e (append xs ys)
6776
68−{-| Useful alias for append -}
69−type (s :: [k]) :++ (t :: [k]) = Append s t
77+{-| Delete elements from a set -}
78+type family (m :: [k]) :\ (x :: k) :: [k] where
79+ '[] :\ x = '[]
80+ (x ': xs) :\ x = xs :\ x
81+ (y ': xs) :\ x = y ': (xs :\ x)
82+
83+class Remove s t where
84+ remove :: Set s -> Proxy t -> Set (s :\ t)
85+
86+instance Remove '[] t where
87+ remove Empty Proxy = Empty
88+
89+instance {-# OVERLAPS #-} Remove xs x => Remove (x ': xs) x where
90+ remove (Ext _ xs) x@Proxy = remove xs x
91+
92+instance {-# OVERLAPPABLE #-} (((y : xs) :\ x) ~ (y : (xs :\ x)), Remove xs x)
93+ => Remove (y ': xs) x where
94+ remove (Ext y xs) (x@Proxy) = Ext y (remove xs x)
7095
7196{-| Splitting a union a set, given the sets we want to split it into -}
7297class Split s t st where
7398 -- where st ~ Union s t
74− split :: Set st -> (Set s, Set t)
99+ split :: Set st -> (Set s, Set t)
75100
76101instance Split '[] '[] '[] where
77102 split Empty = (Empty, Empty)
78103
79−instance Split s t st => Split (x ': s) (x ': t) (x ': st) where
104+instance {-# OVERLAPPABLE #-} Split s t st => Split (x ': s) (x ': t) (x ': st) where
80105 split (Ext x st) = let (s, t) = split st
81106 in (Ext x s, Ext x t)
82107
83−instance Split s t st => Split (x ': s) t (x ': st) where
108+instance {-# OVERLAPS #-} Split s t st => Split (x ': s) t (x ': st) where
84109 split (Ext x st) = let (s, t) = split st
85− in (Ext x s, t)
110+ in (Ext x s, t)
86111
87−instance (Split s t st) => Split s (x ': t) (x ': st) where
112+instance {-# OVERLAPS #-} (Split s t st) => Split s (x ': t) (x ': st) where
88113 split (Ext x st) = let (s, t) = split st
89− in (s, Ext x t)
90−
91−
114+ in (s, Ext x t)
92115
93116{-| Remove duplicates from a sorted list -}
94117type family Nub t where
@@ -98,7 +121,7 @@ type family Nub t where
98121 Nub (e ': f ': s) = e ': Nub (f ': s)
99122
100123{-| Value-level counterpart to the type-level 'Nub'
101− Note: the value-level case for equal types is not define here,
124+ Note: the value-level case for equal types is not define here,
102125 but should be given per-application, e.g., custom 'merging' behaviour may be required -}
103126
104127class Nubable t where
@@ -113,7 +136,7 @@ instance Nubable '[e] where
113136instance Nubable (e ': s) => Nubable (e ': e ': s) where
114137 nub (Ext _ (Ext e s)) = nub (Ext e s)
115138
116−instance (Nub (e ': f ': s) ~ (e ': Nub (f ': s)),
139+instance {-# OVERLAPS #-} (Nub (e ': f ': s) ~ (e ': Nub (f ': s)),
117140 Nubable (f ': s)) => Nubable (e ': f ': s) where
118141 nub (Ext e (Ext f s)) = Ext e (nub (Ext f s))
119142
@@ -122,13 +145,13 @@ instance (Nub (e ': f ': s) ~ (e ': Nub (f ': s)),
122145class Subset s t where
123146 subset :: Set t -> Set s
124147
125−instance Subset '[] '[] where
148+instance Subset '[] '[] where
126149 subset xs = Empty
127150
128−instance Subset '[] (x ': t) where
129− subset xs = Empty
151+instance {-# OVERLAPPABLE #-} Subset s t => Subset s (x ': t) where
152+ subset (Ext _ xs) = subset xs
130153
131−instance Subset s t => Subset (x ': s) (x ': t) where
154+instance {-# OVERLAPS #-} Subset s t => Subset (x ': s) (x ': t) where
132155 subset (Ext x xs) = Ext x (subset xs)
133156
134157
@@ -141,8 +164,8 @@ data Flag = FMin | FMax
141164
142165type family Filter (f :: Flag) (p :: k) (xs :: [k]) :: [k] where
143166 Filter f p '[] = '[]
144− Filter FMin p (x ': xs) = If (Cmp x p == LT) (x ': (Filter FMin p xs)) (Filter FMin p xs)
145− Filter FMax p (x ': xs) = If (Cmp x p == GT || Cmp x p == EQ) (x ': (Filter FMax p xs)) (Filter FMax p xs)
167+ Filter FMin p (x ': xs) = If (Cmp x p == LT) (x ': (Filter FMin p xs)) (Filter FMin p xs)
168+ Filter FMax p (x ': xs) = If (Cmp x p == GT || Cmp x p == EQ) (x ': (Filter FMax p xs)) (Filter FMax p xs)
146169
147170type family DeleteFromList (e :: elem) (list :: [elem]) where
148171 DeleteFromList elem '[] = '[]
@@ -175,11 +198,11 @@ instance FilterV f p '[] where
175198
176199instance (Conder ((Cmp x p) == LT), FilterV FMin p xs) => FilterV FMin p (x ': xs) where
177200 filterV f@Proxy p (Ext x xs) = cond (Proxy::(Proxy ((Cmp x p) == LT)))
178− (Ext x (filterV f p xs)) (filterV f p xs)
201+ (Ext x (filterV f p xs)) (filterV f p xs)
179202
180203instance (Conder (((Cmp x p) == GT) || ((Cmp x p) == EQ)), FilterV FMax p xs) => FilterV FMax p (x ': xs) where
181204 filterV f@Proxy p (Ext x xs) = cond (Proxy::(Proxy (((Cmp x p) == GT) || ((Cmp x p) == EQ))))
182− (Ext x (filterV f p xs)) (filterV f p xs)
205+ (Ext x (filterV f p xs)) (filterV f p xs)
183206
184207class Conder g where
185208 cond :: Proxy g -> Set s -> Set t -> Set (If g s t)
@@ -194,30 +217,20 @@ instance Conder False where
194217
195218type family Cmp (a :: k) (b :: k) :: Ordering
196219
197−{-| Pair a symbol (representing a variable) with a type -}
198−infixl 2 :->
199−data (k :: Symbol) :-> (v :: *) = (Var k) :-> v
200−
201−data Var (k :: Symbol) where Var :: Var k
202− {- Some special defaults for some common names -}
203− X :: Var "x"
204− Y :: Var "y"
205− Z :: Var "z"
206−
207−
208−instance (Show (Var k), Show v) => Show (k :-> v) where
209− show (k :-> v) = "(" ++ show k ++ " :-> " ++ show v ++ ")"
210−instance Show (Var "x") where
211− show X = "x"
212− show Var = "Var"
213−instance Show (Var "y") where
214− show Y = "y"
215− show Var = "Var"
216−instance Show (Var "z") where
217− show Z = "z"
218− show Var = "Var"
219−instance Show (Var v) where
220− show _ = "Var"
221−
222−{-| Symbol comparison -}
223−type instance Cmp (v :-> a) (u :-> b) = CmpSymbol v u
220+{-| Membership of an element in a set, with an acommanying
221+ value-level function that returns a bool -}
222+class Member a s where
223+ member :: Proxy a -> Set s -> Bool
224+
225+instance {-# OVERLAPS #-} Member a (a ': s) where
226+ member _ (Ext x _) = True
227+
228+instance {-# OVERLAPPABLE #-} Member a s => Member a (b ': s) where
229+ member a (Ext _ xs) = member a xs
230+
231+type family MemberP a s :: Bool where
232+ MemberP a '[] = False
233+ MemberP a (a ': s) = True
234+ MemberP a (b ': s) = MemberP a s
235+
236+type NonMember a s = MemberP a s ~ False
stack.yaml+1−1
@@ -1,7 +1,7 @@
11# For more information, see: https://github.com/commercialhaskell/stack/wiki/stack.yaml
22
33# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)
4−resolver: lts-3.5
4+resolver: lts-12.7
55
66# Local packages, usually specified by relative directory name
77packages:
type-level-sets.cabal+59−30
@@ -1,50 +1,77 @@
11name: type-level-sets
2−version: 0.5.1
3−synopsis: Type-level sets (with value-level counterparts and various operations)
4−description:
5− This package provides type-level sets (no duplicates, sorted to provide a nomral form) via 'Set',
6− with value-level counterparts. Described in the paper \"Embedding effect systems in Haskell\" by Dominic Orchard
7− and Tomas Petricek <http://www.cl.cam.ac.uk/~dao29/publ/haskell14-effects.pdf> (Haskell Symposium, 2014)
2+version: 0.8.9.0
3+synopsis: Type-level sets and finite maps (with value-level counterparts)
4+description:
5+ This package provides type-level sets (no duplicates, sorted to provide a normal form) via 'Set' and type-level
6+ finite maps via 'Map', with value-level counterparts.
87 .
9− Here is a brief example:
8+ Described in the paper \"Embedding effect systems in Haskell\" by Dominic Orchard
9+ and Tomas Petricek <http://www.cl.cam.ac.uk/~dao29/publ/haskell14-effects.pdf> (Haskell Symposium, 2014). This version now uses Quicksort to normalise the representation.
10+ .
11+ Here is a brief example for finite maps:
1012 .
1113 >
12− > import Data.Type.Set
14+ > import Data.Type.Map
15+ >
16+ > -- Specify how to combine duplicate key-value pairs for Int values
17+ > type instance Combine Int Int = Int
18+ > instance Combinable Int Int where
19+ > combine x y = x + y
20+ >
21+ > foo :: Map '["x" :-> Int, "z" :-> Bool, "w" :-> Int]
22+ > foo = Ext (Var :: (Var "x")) 2
23+ > $ Ext (Var :: (Var "z")) True
24+ > $ Ext (Var :: (Var "w")) 5
25+ > $ Empty
1326 >
14− > foo :: Set '["x" :-> Int, "z" :-> Int, "w" :-> Int]
15− > foo = Ext ((Var :: (Var "x")) :-> 2) $
16− > Ext ((Var :: (Var "z")) :-> 4) $
17− > Ext ((Var :: (Var "w")) :-> 5) $
18− > Empty
19− >
20− > bar :: Set '["y" :-> Int, "w" :-> Int]
21− > bar = Ext ((Var :: (Var "y")) :-> 3) $
22− > Ext ((Var :: (Var "w")) :-> 1) $
23− > Empty
24− >
25− > -- foobar :: Set '["w" :-> Int, "x" :-> Int, "y" :-> Int, "z" :-> Int]
27+ > bar :: Map '["y" :-> Int, "w" :-> Int]
28+ > bar = Ext (Var :: (Var "y")) 3
29+ > $ Ext (Var :: (Var "w")) 1
30+ > $ Empty
31+ >
32+ > -- foobar :: Map '["w" :-> Int, "x" :-> Int, "y" :-> Int, "z" :-> Bool]
2633 > foobar = foo `union` bar
2734 .
28− The 'Set' type for 'foobar' here shows the normalised form (sorted with no duplicates).
35+ The 'Map' type for 'foobar' here shows the normalised form (sorted with no duplicates).
2936 The type signatures is commented out as it can be infered. Running the example we get:
3037 .
31− > >>> foobar
32− > [(Var :-> 1), (Var :-> 2), (Var :-> 3), (Var :-> 4)]
38+ > >>> foobar
39+ > {w :-> 6, x :-> 2, y :-> 3, z :-> True}
40+ .
41+ Thus, we see that the values for \"w\" are added together.
42+ For sets, here is an example:
43+ .
44+ > import GHC.TypeLits
45+ > import Data.Type.Set
46+ > type instance Cmp (Natural n) (Natural m) = CmpNat n m
47+ >
48+ > data Natural (a :: Nat) where
49+ > Z :: Natural 0
50+ > S :: Natural n -> Natural (n + 1)
51+ >
52+ > -- foo :: Set '[Natural 0, Natural 1, Natural 3]
53+ > foo = asSet $ Ext (S Z) (Ext (S (S (S Z))) (Ext Z Empty))
54+ >
55+ > -- bar :: Set '[Natural 1, Natural 2]
56+ > bar = asSet $ Ext (S (S Z)) (Ext (S Z) (Ext (S Z) Empty))
57+ >
58+ > -- foobar :: Set '[Natural 0, Natural 1, Natural 2, Natural 3]
59+ > foobar = foo `union` bar
3360 .
34− Thus, we see that the first value paired with the \"w\" variable is dropped.
61+ Note the types here are all inferred.
3562 .
3663license: BSD3
3764license-file: LICENSE
3865category: Type System, Data Structures
39−copyright: 2013-14 University of Cambridge
66+copyright: 2013-18 University of Kent
4067author: Dominic Orchard
4168maintainer: Dominic Orchard
4269stability: experimental
4370build-type: Simple
4471cabal-version: >= 1.6
45−tested-with: GHC == 7.8.2
72+tested-with: GHC >= 8.2.2
4673
47−extra-source-files: example.hs
74+extra-source-files: example.hs, example2.hs, changelog.md
4875
4976
5077source-repository head
@@ -53,9 +80,11 @@ source-repository head
5380
5481library
5582 hs-source-dirs: src
83+ other-extensions: TypeInType
5684
5785
5886 exposed-modules: Data.Type.Set
59−
60− build-depends: base >= 4.7.0.0 && < 5,
61− ghc-prim
87+ Data.Type.Map
88+
89+ build-depends: base < 5,
90+ ghc-prim