on master help sign in
Add sum subtyping and documentation.
commit7665edc9d8293af6ae1226ea4bc71971ee90805c authorWill Jones <will@sacharissa.co.uk> dateWed 2 Aug 2017, 21:16 (9 years ago) parent6fd4bd9
8 files changed+204−49
src/Data/Generics/Internal/Families/Count.hs+1−2 src/Data/Generics/Internal/Families/Has.hs+1−2 src/Data/Generics/Product/Subtype.hs+32−33 src/Data/Generics/Product/Typed.hs+3−3 src/Data/Generics/Sum/Any.hs+6−4 src/Data/Generics/Sum/Constructors.hs+2−2 src/Data/Generics/Sum/Subtype.hs+154 src/Data/Generics/Sum/Typed.hs+5−3
src/Data/Generics/Internal/Families/Count.hs+1−2
@@ -20,10 +20,9 @@ module Data.Generics.Internal.Families.Count
2020 , Count (..)
2121 ) where
2222
23−import GHC.Generics
24−
2523import Data.Type.Bool (If)
2624import Data.Type.Equality (type (==))
25+import GHC.Generics
2726import GHC.TypeLits (TypeError, ErrorMessage (..))
2827
2928import Data.Generics.Internal.HList
src/Data/Generics/Internal/Families/Has.hs+1−2
@@ -21,10 +21,9 @@ module Data.Generics.Internal.Families.Has
2121 , HasCtorP
2222 ) where
2323
24−import GHC.Generics
25−
2624import Data.Type.Bool (type (||), type (&&))
2725import Data.Type.Equality (type (==))
26+import GHC.Generics
2827import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
2928
3029import Data.Generics.Internal.HList
src/Data/Generics/Product/Subtype.hs+32−33
@@ -21,36 +21,15 @@
2121-- Stability : experimental
2222-- Portability : non-portable
2323--
24−-- Structural subtype relationship between record types.
25−--
26−-- The running example in this module is the following two types:
27−--
28−-- @
29−--
30−-- module Test where
31−--
32−-- import GHC.Generics
33−-- import Data.Generics.Record
34−--
35−-- data Human = Human
36−-- { name :: String
37−-- , age :: Int
38−-- , address :: String
39−-- } deriving (Generic, Show)
40−--
41−-- data Animal = Animal
42−-- { name :: String
43−-- , age :: Int
44−-- } deriving (Generic, Show)
45−--
46−-- human :: Human
47−-- human = Human \"Tunyasz\" 50 \"London\"
48−--
49−-- @
24+-- Structural subtype relationships between product types.
5025--
5126-----------------------------------------------------------------------------
27+
5228module Data.Generics.Product.Subtype
53− ( Subtype (..)
29+ ( -- *Lenses
30+ --
31+ -- $example
32+ Subtype (..)
5433 ) where
5534
5635import Data.Generics.Internal.Families
@@ -60,14 +39,36 @@ import Data.Generics.Product.Fields
6039import Data.Kind (Type)
6140import GHC.Generics
6241
42+-- $example
43+-- @
44+-- module Example where
45+--
46+-- import Data.Generics.Product
47+-- import GHC.Generics
48+--
49+-- data Human = Human
50+-- { name :: String
51+-- , age :: Int
52+-- , address :: String
53+-- } deriving (Generic, Show)
54+--
55+-- data Animal = Animal
56+-- { name :: String
57+-- , age :: Int
58+-- } deriving (Generic, Show)
59+--
60+-- human :: Human
61+-- human = Human \"Tunyasz\" 50 \"London\"
62+-- @
63+
6364-- |Structural subtype relationship
6465--
6566-- @sub@ is a (structural) `subtype' of @sup@, if its fields are a subset of
6667-- those of @sup@.
6768--
6869class Subtype sup sub where
69− -- | Structural subtype lens. Given a subtype relationship @sub :< sup@,
70− -- we can focus on the @sub@ structure of @sup@.
70+ -- |Structural subtype lens. Given a subtype relationship @sub :< sup@,
71+ -- we can focus on the @sub@ structure of @sup@.
7172 --
7273 -- >>> human ^. super @Animal
7374 -- Animal {name = "Tunyasz", age = 50}
@@ -78,7 +79,7 @@ class Subtype sup sub where
7879 super f sub
7980 = fmap (`smash` sub) (f (upcast sub))
8081
81− -- | Cast the more specific subtype to the more general supertype
82+ -- |Cast the more specific subtype to the more general supertype
8283 --
8384 -- >>> upcast human :: Animal
8485 -- Animal {name = "Tunyasz", age = 50}
@@ -86,7 +87,7 @@ class Subtype sup sub where
8687 upcast :: sub -> sup
8788 upcast s = s ^. super @sup
8889
89− -- | Plug a smaller structure into a larger one
90+ -- |Plug a smaller structure into a larger one
9091 --
9192 -- >>> smash (Animal "dog" 10) human
9293 -- Human {name = "dog", age = 10, address = "London"}
@@ -95,7 +96,6 @@ class Subtype sup sub where
9596
9697 {-# MINIMAL super | smash, upcast #-}
9798
98−-- | Instances are created by the compiler
9999instance
100100 ( GSmash (Rep a) (Rep b)
101101 , GUpcast (Rep a) (Rep b)
@@ -108,7 +108,6 @@ instance
108108--------------------------------------------------------------------------------
109109-- * Generic upcasting
110110
111−-- | Upcast 'sub to 'sup' (generic rep)
112111class GUpcast (sub :: Type -> Type) (sup :: Type -> Type) where
113112 gupcast :: sub p -> sup p
114113
src/Data/Generics/Product/Typed.hs+3−3
@@ -58,7 +58,7 @@ import GHC.TypeLits (TypeError, ErrorMessage (..))
5858-- human = Human \"Tunyasz\" 50 \"London\"
5959-- @
6060
61−-- | Records that have a field with a unique type.
61+-- |Records that have a field with a unique type.
6262class HasType a s where
6363 -- |A lens that focuses on a field with a unique type in its parent type.
6464 -- Compatible with the lens package's 'Control.Lens.Lens' type.
@@ -69,11 +69,11 @@ class HasType a s where
6969 typed f t
7070 = fmap (flip (setTyped @a) t) (f (getTyped @a t))
7171
72− -- |Get field at type
72+ -- |Get field at type.
7373 getTyped :: s -> a
7474 getTyped s = s ^. typed @a
7575
76− -- |Set field at type
76+ -- |Set field at type.
7777 setTyped :: a -> s -> s
7878 setTyped = set (typed @a)
7979
src/Data/Generics/Sum/Any.hs+6−4
@@ -41,7 +41,7 @@ import Data.Generics.Sum.Typed
4141--
4242-- data Animal
4343-- = Dog Dog
44−-- | Cat (Name, Age)
44+-- | Cat Name Age
4545-- | Duck Age
4646-- deriving (Generic, Show)
4747--
@@ -57,7 +57,7 @@ import Data.Generics.Sum.Typed
5757-- dog, cat, duck :: Animal
5858--
5959-- dog = Dog (MkDog "Shep" 3)
60−-- cat = Cat ("Mog", 5)
60+-- cat = Cat "Mog" 5
6161-- duck = Duck 2
6262-- @
6363
@@ -73,10 +73,12 @@ class AsAny (sel :: k) a s | s sel k -> a where
7373 -- Just (MkDog {name = "Shep", age = 3})
7474 -- >>> dog ^? _As @"Cat"
7575 -- Nothing
76+ -- >>> cat ^? _As @(Name, Age)
77+ -- Just ("Mog",5)
7678 -- >>> cat ^? _As @"Cat"
77− -- Just ("Mog", 5)
79+ -- Just ("Mog",5)
7880 -- >>> _As @"Cat" # ("Garfield", 6) :: Animal
79− -- Cat ("Garfield", 6)
81+ -- Cat ("Garfield",6)
8082 -- >>> duck ^? _As @Age
8183 -- Just 2
8284 _As :: Prism' s a
src/Data/Generics/Sum/Constructors.hs+2−2
@@ -47,7 +47,7 @@ import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
4747--
4848-- data Animal
4949-- = Dog Dog
50−-- | Cat (Name, Age)
50+-- | Cat Name Age
5151-- | Duck Age
5252-- deriving (Generic, Show)
5353--
@@ -63,7 +63,7 @@ import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
6363-- dog, cat, duck :: Animal
6464--
6565-- dog = Dog (MkDog "Shep" 3)
66−-- cat = Cat ("Mog", 5)
66+-- cat = Cat "Mog" 5
6767-- duck = Duck 2
6868-- @
6969
src/Data/Generics/Sum/Subtype.hsnew file+154
@@ -0,0 +1,154 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeApplications #-}
7+{-# LANGUAGE TypeOperators #-}
8+{-# LANGUAGE TypeSynonymInstances #-}
9+{-# LANGUAGE UndecidableInstances #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Sum.Subtype
14+-- Copyright : (C) 2017 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Structural subtype relationships between sum types.
21+--
22+-----------------------------------------------------------------------------
23+
24+module Data.Generics.Sum.Subtype
25+ ( -- *Prisms
26+ --
27+ -- $example
28+ AsSubtype (..)
29+ ) where
30+
31+import Data.Generics.Internal.HList
32+import Data.Generics.Internal.Lens
33+import Data.Generics.Sum.Typed
34+
35+import Data.Kind
36+import GHC.Generics
37+
38+-- $example
39+-- @
40+-- module Example where
41+--
42+-- import Data.Generics.Sum
43+-- import GHC.Generics
44+--
45+-- data Animal
46+-- = Dog Dog
47+-- | Cat Name Age
48+-- | Duck Age
49+-- deriving (Generic, Show)
50+--
51+-- data FourLeggedAnimal
52+-- = Dog4 Dog
53+-- | Cat4 Name Age
54+-- deriving (Generic, Show)
55+--
56+-- data Dog = MkDog
57+-- { name :: Name
58+-- , age :: Age
59+-- }
60+-- deriving (Generic, Show)
61+--
62+-- type Name = String
63+-- type Age = Int
64+--
65+-- dog, cat, duck :: Animal
66+--
67+-- dog = Dog (MkDog "Shep" 3)
68+-- cat = Cat "Mog" 5
69+-- duck = Duck 2
70+--
71+-- dog4, cat4 :: FourLeggedAnimal
72+--
73+-- dog4 = Dog4 (MkDog "Snowy" 4)
74+-- cat4 = Cat4 "Garfield" 6
75+-- @
76+
77+-- |Structural subtyping between sums. A sum 'Sub' is a subtype of another sum
78+-- 'Sup' if a value of 'Sub' can be given (modulo naming of constructors)
79+-- whenever a value of 'Sup' is expected. In the running example for instance,
80+-- 'FourLeggedAnimal` is a subtype of 'Animal' since a value of the former can
81+-- be given as a value of the latter (renaming 'Dog4' to 'Dog' and 'Cat4' to
82+-- 'Cat').
83+class AsSubtype sub sup where
84+ -- |A prism that captures structural subtyping. Allows a substructure to be
85+ -- injected (upcast) into a superstructure or a superstructure to be downcast
86+ -- into a substructure (which may fail).
87+ --
88+ -- >>> _Sub # dog4 :: Animal
89+ -- Dog (MkDog {name = "Snowy", age = 4})
90+ -- >>> cat ^? _Sub :: Maybe FourLeggedAnimal
91+ -- Just (Cat4 "Mog" 5)
92+ -- >>> duck ^? _Sub :: Maybe FourLeggedAnimal
93+ -- Nothing
94+ _Sub :: Prism' sup sub
95+ _Sub = prism injectSub projectSub
96+
97+ -- |Injects a subtype into a supertype (upcast).
98+ injectSub :: sub -> sup
99+
100+ -- |Projects a subtype from a supertype (downcast).
101+ projectSub :: sup -> Either sup sub
102+
103+ {-# MINIMAL injectSub, projectSub #-}
104+
105+instance
106+ ( Generic sub
107+ , Generic sup
108+ , GAsSubtype (Rep sub) (Rep sup)
109+ ) => AsSubtype sub sup where
110+
111+ injectSub = to . ginjectSub . from
112+ projectSub = either (Left . to) (Right . to) . gprojectSub . from
113+
114+-- |As 'AsSubtype' but over generic representations as defined by
115+-- "GHC.Generics".
116+class GAsSubtype (subf :: Type -> Type) (supf :: Type -> Type) where
117+ ginjectSub :: subf x -> supf x
118+ gprojectSub :: supf x -> Either (supf x) (subf x)
119+
120+instance
121+ ( GAsSubtype l supf
122+ , GAsSubtype r supf
123+ ) => GAsSubtype (l :+: r) supf where
124+
125+ ginjectSub x = case x of
126+ L1 l -> ginjectSub l
127+ R1 r -> ginjectSub r
128+ gprojectSub x
129+ = case gprojectSub x of
130+ Left _ -> fmap R1 (gprojectSub x)
131+ Right y -> Right (L1 y)
132+
133+instance
134+ ( GAsType supf a
135+ , GCollectible subf as
136+ , ListTuple a as
137+ ) => GAsSubtype (C1 meta subf) supf where
138+
139+ ginjectSub
140+ = ginjectTyped . listToTuple . gtoCollection . unM1
141+ gprojectSub
142+ = fmap (M1 . gfromCollection . tupleToList) . gprojectTyped
143+
144+instance GAsType supf a => GAsSubtype (S1 meta (Rec0 a)) supf where
145+ ginjectSub
146+ = ginjectTyped @supf . unK1 . unM1
147+ gprojectSub
148+ = fmap (M1 . K1) . gprojectTyped @supf
149+
150+instance GAsSubtype subf supf => GAsSubtype (D1 meta subf) supf where
151+ ginjectSub
152+ = ginjectSub . unM1
153+ gprojectSub
154+ = fmap M1 . gprojectSub
src/Data/Generics/Sum/Typed.hs+5−3
@@ -69,7 +69,7 @@ import Data.Generics.Internal.Lens
6969-- dog, cat, duck :: Animal
7070--
7171-- dog = Dog (MkDog "Shep" 3)
72−-- cat = Cat ("Mog", 5)
72+-- cat = Cat "Mog" 5
7373-- duck = Duck 2
7474-- @
7575
@@ -80,8 +80,10 @@ class AsType a s where
8080 --
8181 -- >>> dog ^? _Typed @Dog
8282 -- Just (MkDog {name = "Shep", age = 3})
83− -- >>> dog ^? _Typed @Cat
83+ -- >>> dog ^? _Typed @Age
8484 -- Nothing
85+ -- >>> cat ^? _Typed @(Name, Age)
86+ -- Just ("Mog",5)
8587 -- >>> duck ^? _Typed @Age
8688 -- Just 2
8789 _Typed :: Prism' s a
@@ -131,7 +133,7 @@ instance
131133 ginjectTyped
132134 = M1 . gfromCollection . tupleToList
133135 gprojectTyped
134− = Right . listToTuple @_ @as . gtoCollection . unM1
136+ = Right . listToTuple . gtoCollection . unM1
135137
136138instance GSumAsType (HasPartialTypeTupleP a l) l r a => GAsType (l :+: r) a where
137139 ginjectTyped