@@ -1,14 +1,16 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE NoMonomorphismRestriction #-}
5−{-# LANGUAGE PolyKinds #-}
7+{-# LANGUAGE PolyKinds #-}
6−{-# LANGUAGE Rank2Types #-}
8+{-# LANGUAGE Rank2Types #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
10+{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
12+{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE UndecidableInstances #-}
13-----------------------------------------------------------------------------
15-----------------------------------------------------------------------------
@@ -49,7 +51,6 @@
49-----------------------------------------------------------------------------
51-----------------------------------------------------------------------------
50module Data.Generics.Product.Subtype
52module Data.Generics.Product.Subtype
51 ( Subtype (..)
53 ( Subtype (..)
55import Data.Generics.Product.Fields
56import Data.Generics.Product.Fields
@@ -65,34 +66,45 @@ import GHC.Generics
65-- @sub@ is a (structural) `subtype' of @sup@, if its fields are a subset of
66-- @sub@ is a (structural) `subtype' of @sup@, if its fields are a subset of
66-- those of @sup@.
67-- those of @sup@.
68−class Subtype sub sup where
69+class Subtype sup sub where
70+ -- | Structural subtype lens. Given a subtype relationship @sub :< sup@,
71+ -- we can focus on the @sub@ structure of @sup@.
73+ -- >>> human ^. super @Animal
74+ -- Animal {name = "Tunyasz", age = 50}
76+ -- >>> set (super @Animal) (Animal "dog" 10) human
77+ -- Human {name = "dog", age = 10, address = "London"}
78+ super :: Lens' sub sup
80+ = fmap (`smash` sub) (f (upcast sub))
69 -- | Cast the more specific subtype to the more general supertype
82 -- | Cast the more specific subtype to the more general supertype
71 -- >>> upcast human :: Animal
84 -- >>> upcast human :: Animal
72 -- Animal {name = "Tunyasz", age = 50}
85 -- Animal {name = "Tunyasz", age = 50}
74 upcast :: sub -> sup
87 upcast :: sub -> sup
88+ upcast s = s ^. super @sup
75 -- | Plug a smaller structure into a larger one
90 -- | Plug a smaller structure into a larger one
77 -- >>> smash (Animal "dog" 10) human
92 -- >>> smash (Animal "dog" 10) human
78 -- Human {name = "dog", age = 10, address = "London"}
93 -- Human {name = "dog", age = 10, address = "London"}
79 smash :: sup -> sub -> sub
94 smash :: sup -> sub -> sub
95+ smash = set (super @sup)
81−-- | Instances are created by the compiler
97+ {-# MINIMAL super | smash, upcast #-}
82−instance (GSmash (Rep a) (Rep b), GUpcast (Rep a) (Rep b), Generic a, Generic b) => Subtype a b where
83− upcast = to . gupcast . from
84− smash p b = to $ gsmash (from p) (from b)
86−-- | Structural subtype lens. Given a subtype relationship @sub :< sup@,
99+-- | Instances are created by the compiler
87−-- we can focus on the @sub@ structure of @sup@.
100+instance
88−--
101+ ( GSmash (Rep a) (Rep b)
89−-- >>> human ^. super @Animal
102+ , GUpcast (Rep a) (Rep b)
90−-- Animal {name = "Tunyasz", age = 50}
103+ , Generic a
92−-- >>> set (super @Animal) (Animal "dog" 10) human
105+ ) => Subtype b a where
93−-- Human {name = "dog", age = 10, address = "London"}
106+ smash p b = to $ gsmash (from p) (from b)
94−super :: forall sup sub. Subtype sub sup => Lens' sub sup
107+ upcast = to . gupcast . from
95−super f sub = fmap (`smash` sub) (f (upcast sub))
97--------------------------------------------------------------------------------
109--------------------------------------------------------------------------------
98-- * Generic upcasting
110-- * Generic upcasting
@@ -104,10 +116,16 @@ class GUpcast (sub :: Type -> Type) (sup :: Type -> Type) where
104instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
116instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
105 gupcast rep = gupcast rep :*: gupcast rep
117 gupcast rep = gupcast rep :*: gupcast rep
120+ GHasField field sub t
107−instance {-# OVERLAPPING #-} GHasField field sub t => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
121+ => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
108 gupcast r = M1 (K1 (r ^. gfield @field))
123 gupcast r = M1 (K1 (r ^. gfield @field))
110−instance GUpcast sub sup => GUpcast sub (M1 i c sup) where
125+instance GUpcast sub sup => GUpcast sub (C1 c sup) where
126+ gupcast = M1 . gupcast
128+instance GUpcast sub sup => GUpcast sub (D1 c sup) where
111 gupcast = M1 . gupcast
129 gupcast = M1 . gupcast
113--------------------------------------------------------------------------------
131--------------------------------------------------------------------------------
@@ -119,19 +137,25 @@ class GSmash sub sup where
119instance (GSmash a sup, GSmash b sup) => GSmash (a :*: b) sup where
137instance (GSmash a sup, GSmash b sup) => GSmash (a :*: b) sup where
120 gsmash rep (a :*: b) = gsmash rep a :*: gsmash rep b
138 gsmash rep (a :*: b) = gsmash rep a :*: gsmash rep b
122−instance {-# OVERLAPPING #-}
140+instance
123 ( leaf ~ (S1 ('MetaSel ('Just field) p f b) t)
141 ( leaf ~ (S1 ('MetaSel ('Just field) p f b) t)
124 , GSmashLeaf leaf sup (HasFieldP field sup)
142 , GSmashLeaf leaf sup (HasFieldP field sup)
125 ) => GSmash (S1 ('MetaSel ('Just field) p f b) t) sup where
143 ) => GSmash (S1 ('MetaSel ('Just field) p f b) t) sup where
126 gsmash = gsmashLeaf @_ @_ @(HasFieldP field sup)
145 gsmash = gsmashLeaf @_ @_ @(HasFieldP field sup)
128−instance GSmash sub sup => GSmash (M1 i c sub) sup where
147+instance GSmash sub sup => GSmash (C1 c sub) sup where
148+ gsmash sup (M1 sub) = M1 (gsmash sup sub)
150+instance GSmash sub sup => GSmash (D1 c sub) sup where
129 gsmash sup (M1 sub) = M1 (gsmash sup sub)
151 gsmash sup (M1 sub) = M1 (gsmash sup sub)
131class GSmashLeaf sub sup (w :: Bool) where
153class GSmashLeaf sub sup (w :: Bool) where
132 gsmashLeaf :: sup p -> sub p -> sub p
154 gsmashLeaf :: sup p -> sub p -> sub p
157+ GHasField field sup t
134−instance (GHasField field sup t) => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
158+ => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
135 gsmashLeaf sup (M1 (K1 _)) = M1 (K1 (sup ^. gfield @field))
159 gsmashLeaf sup (M1 (K1 _)) = M1 (K1 (sup ^. gfield @field))
137instance GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'False where
161instance GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'False where