@@ -24,22 +24,60 @@ import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
24import qualified Data.Text as Text
24import qualified Data.Text as Text
27+-- | @TypeInformation@ describes how to encode and decode a Haskell type to and
28+-- from database queries. The @typeName@ is the name of the type in the
29+-- database, which is used to accurately type literals.
27type TypeInformation :: Type -> Type
30type TypeInformation :: Type -> Type
28data TypeInformation a where
31data TypeInformation a where
29 TypeInformation ::
32 TypeInformation ::
30 { encode :: a -> Opaleye.PrimExpr
33 { encode :: a -> Opaleye.PrimExpr
34+ -- ^ How to encode a single Haskell value as a SQL expression.
31 , decode :: Hasql.Value x
35 , decode :: Hasql.Value x
36+ -- ^ How to deserialize a single result back to Haskell.
32 , typeName :: String
37 , typeName :: String
38+ -- ^ The name of the SQL type.
33 , out :: x -> a
39 , out :: x -> a
40+ -- ^ A final output function - usually this will just be 'id'. This is
41+ -- needed to allow @TypeInformation@s to be coerced.
34 } -> TypeInformation a
42 } -> TypeInformation a
45+-- | Simultaneously map over how a type is both encoded and decoded, while
46+-- retaining the name of the type. This operation is useful if you want to
47+-- essentially @newtype@ another 'DBType'.
49+-- The mapping is required to be total. If you have a partial mapping, see
50+-- 'parseTypeInformation'.
37mapTypeInformation :: ()
51mapTypeInformation :: ()
38 => (a -> b) -> (b -> a)
52 => (a -> b) -> (b -> a)
39 -> TypeInformation a -> TypeInformation b
53 -> TypeInformation a -> TypeInformation b
40mapTypeInformation = parseTypeInformation . fmap pure
54mapTypeInformation = parseTypeInformation . fmap pure
57+-- | Apply a parser to 'TypeInformation'.
59+-- This can be used if the data stored in the database should only be subset of
60+-- a given 'TypeInformation'. The parser is applied when deserializing rows
61+-- returned - the encoder assumes that the input data is already in the
64+-- One example where this may be useful is with a database that stores data in
65+-- some legacy encoding:
67+-- >>> import Data.Text (Text)
69+-- >>> data Color = Red | Green | Blue
71+-- instance DBType Color where
72+-- typeInformation = parseTypeInformation parseLegacy toLegacy typeInformation
74+-- parseLegacy :: Text -> Either String Color
75+-- parseLegacy "red" = Right Red
76+-- parseLegacy "green" = Right Green
77+-- parseLegacy _ = Left "Unexpected Color"
78+-- toLegacy Red = "red"
79+-- toLegacy Green = "green"
43parseTypeInformation :: ()
81parseTypeInformation :: ()
44 => (a -> Either String b) -> (b -> a)
82 => (a -> Either String b) -> (b -> a)
45 -> TypeInformation a -> TypeInformation b
83 -> TypeInformation a -> TypeInformation b