@@ -1,14 +1,16 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE PolyKinds #-}
6−{-# LANGUAGE Rank2Types #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE NoMonomorphismRestriction #-}
7+{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE Rank2Types #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeApplications #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-# LANGUAGE UndecidableInstances #-}
1214
1315-----------------------------------------------------------------------------
1416-- |
@@ -49,7 +51,6 @@
4951-----------------------------------------------------------------------------
5052module Data.Generics.Product.Subtype
5153 ( Subtype (..)
52− , super
5354 ) where
5455
5556import Data.Generics.Product.Fields
@@ -65,34 +66,45 @@ import GHC.Generics
6566-- @sub@ is a (structural) `subtype' of @sup@, if its fields are a subset of
6667-- those of @sup@.
6768--
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@.
72+ --
73+ -- >>> human ^. super @Animal
74+ -- Animal {name = "Tunyasz", age = 50}
75+ --
76+ -- >>> set (super @Animal) (Animal "dog" 10) human
77+ -- Human {name = "dog", age = 10, address = "London"}
78+ super :: Lens' sub sup
79+ super f sub
80+ = fmap (`smash` sub) (f (upcast sub))
81+
6982 -- | Cast the more specific subtype to the more general supertype
7083 --
7184 -- >>> upcast human :: Animal
7285 -- Animal {name = "Tunyasz", age = 50}
7386 --
7487 upcast :: sub -> sup
88+ upcast s = s ^. super @sup
89+
7590 -- | Plug a smaller structure into a larger one
7691 --
7792 -- >>> smash (Animal "dog" 10) human
7893 -- Human {name = "dog", age = 10, address = "London"}
7994 smash :: sup -> sub -> sub
95+ smash = set (super @sup)
8096
81−-- | Instances are created by the compiler
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)
97+ {-# MINIMAL super | smash, upcast #-}
8598
86−-- | Structural subtype lens. Given a subtype relationship @sub :< sup@,
87−-- we can focus on the @sub@ structure of @sup@.
88−--
89−-- >>> human ^. super @Animal
90−-- Animal {name = "Tunyasz", age = 50}
91−--
92−-- >>> set (super @Animal) (Animal "dog" 10) human
93−-- Human {name = "dog", age = 10, address = "London"}
94−super :: forall sup sub. Subtype sub sup => Lens' sub sup
95−super f sub = fmap (`smash` sub) (f (upcast sub))
99+-- | Instances are created by the compiler
100+instance
101+ ( GSmash (Rep a) (Rep b)
102+ , GUpcast (Rep a) (Rep b)
103+ , Generic a
104+ , Generic b
105+ ) => Subtype b a where
106+ smash p b = to $ gsmash (from p) (from b)
107+ upcast = to . gupcast . from
96108
97109--------------------------------------------------------------------------------
98110-- * Generic upcasting
@@ -104,10 +116,16 @@ class GUpcast (sub :: Type -> Type) (sup :: Type -> Type) where
104116instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
105117 gupcast rep = gupcast rep :*: gupcast rep
106118
107−instance {-# OVERLAPPING #-} GHasField field sub t => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
119+instance
120+ GHasField field sub t
121+ => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
122+
108123 gupcast r = M1 (K1 (r ^. gfield @field))
109124
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
127+
128+instance GUpcast sub sup => GUpcast sub (D1 c sup) where
111129 gupcast = M1 . gupcast
112130
113131--------------------------------------------------------------------------------
@@ -119,19 +137,25 @@ class GSmash sub sup where
119137instance (GSmash a sup, GSmash b sup) => GSmash (a :*: b) sup where
120138 gsmash rep (a :*: b) = gsmash rep a :*: gsmash rep b
121139
122−instance {-# OVERLAPPING #-}
140+instance
123141 ( leaf ~ (S1 ('MetaSel ('Just field) p f b) t)
124142 , GSmashLeaf leaf sup (HasFieldP field sup)
125143 ) => GSmash (S1 ('MetaSel ('Just field) p f b) t) sup where
144+
126145 gsmash = gsmashLeaf @_ @_ @(HasFieldP field sup)
127146
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)
149+
150+instance GSmash sub sup => GSmash (D1 c sub) sup where
129151 gsmash sup (M1 sub) = M1 (gsmash sup sub)
130152
131153class GSmashLeaf sub sup (w :: Bool) where
132154 gsmashLeaf :: sup p -> sub p -> sub p
133155
134−instance (GHasField field sup t) => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
156+instance
157+ GHasField field sup t
158+ => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
135159 gsmashLeaf sup (M1 (K1 _)) = M1 (K1 (sup ^. gfield @field))
136160
137161instance GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'False where