@@ -32,11 +32,14 @@ module Data.Generics.Product.Subtype
3232 Subtype (..)
3333 ) where
3434
35+import Data.Generics.Internal.Families
3536import Data.Generics.Internal.Lens
3637import Data.Generics.Internal.Void
3738import Data.Generics.Product.Internal.Subtype
3839
3940import GHC.Generics (Generic (Rep, to, from) )
41+import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
42+import Data.Kind (Type, Constraint)
4043
4144-- $setup
4245-- == /Running example:/
@@ -86,6 +89,12 @@ class Subtype sup sub where
8689 -- >>> upcast human :: Animal
8790 -- Animal {name = "Tunyasz", age = 50}
8891 --
92+ -- >>> upcast (upcast human :: Animal) :: Human
93+ -- ...
94+ -- ... The type 'Human' is not a subtype of 'Animal'.
95+ -- ... The following fields are missing from 'Human':
96+ -- ... address
97+ -- ...
8998 upcast :: sub -> sup
9099 upcast s = s ^. super @sup
91100
@@ -98,12 +107,12 @@ class Subtype sup sub where
98107
99108 {-# MINIMAL super | smash, upcast #-}
100109
101−-- TODO: improve type error by showing a diff of fields
102110instance
103111 ( Generic a
104112 , Generic b
105113 , GSmash (Rep a) (Rep b)
106114 , GUpcast (Rep a) (Rep b)
115+ , ErrorUnless b a (CollectFieldsOrdered (Rep b) \\ CollectFieldsOrdered (Rep a))
107116 ) => Subtype b a where
108117 smash p b = to $ gsmash (from p) (from b)
109118 upcast = to . gupcast . from
@@ -113,3 +122,18 @@ instance {-# OVERLAPPING #-} Subtype a Void where
113122 super = undefined
114123instance {-# OVERLAPPING #-} Subtype Void a where
115124 super = undefined
125+
126+type family ErrorUnless (sub :: Type) (sup :: Type) (diff :: [Symbol]) :: Constraint where
127+ ErrorUnless _ _ '[]
128+ = ()
129+
130+ ErrorUnless sub sup fs
131+ = TypeError
132+ ( 'Text "The type '"
133+ ':<>: 'ShowType sub
134+ ':<>: 'Text "' is not a subtype of '"
135+ ':<>: 'ShowType sup ':<>: 'Text "'."
136+ ':$$: 'Text "The following fields are missing from '"
137+ ':<>: 'ShowType sub ':<>: 'Text "':"
138+ ':$$: ShowSymbols fs
139+ )