-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement generic derivations for
QueryParams
and QueryResults
I moved `convertError` from `Database.MySQL.Simpe.QueryResults` into `Database.MySQL.Simple.Result` to avoid a cyclic dependency between the `Database.MySQL.Simpe.QueryResults` and `Database.MySQL.Simpe.QueryResults.Generic` that both make use of said function. Note that this change requires GHC Haskell. We may want to hide this behind a preprocessor flag for that reason. I don't quite know if it makes sense to derive instances for `U1` (types with a single constructor and no fields). Although it is certainly helpful during development to not get a type error when trying to derive instances for a type that you are in the process of defining, like: data User = User I have also noted a concern with my generic implementation for `(:*:)`. I'll reproduce it here: I'm concerned about this implementation since it's biased towards one side. Meaning that it's expecting the chain of `(:*:)` to lean to the right. As an example of the problem consider the following: > data T = T Int Int Int deriving (Generic) > from (T 1 2 3) M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: (M1 {unM1 = K1 {unK1 = 2}} :*: M1 {unM1 = K1 {unK1 = 3}})}} If the result in stead had been like this (note the re-bracketing): M1 {unM1 = (M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = 2}}}) :*: M1 {unM1 = K1 {unK1 = 3}}} Then the generic derivation for `T` would fail. Furthermore, to be able to supply the arity in the call to `convertError` I think we might need a `Typeable (a :*: b)` constraint. Lastly, the existing implementations for `QueryResults` uses strictness annotations in certain places. I don't know if these are strictly (pun!) necessary and haven't used them in the generic implementation.
- Loading branch information
Showing
6 changed files
with
170 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{-# LANGUAGE ScopedTypeVariables, TypeOperators, InstanceSigs, KindSignatures #-} | ||
module Database.MySQL.Simple.QueryParams.Generic | ||
( QueryParams(..) | ||
) where | ||
|
||
import Data.Kind | ||
import GHC.Generics | ||
|
||
import Database.MySQL.Simple.Param (Action(..), Param(..)) | ||
|
||
class QueryParams (f :: Type -> Type) where | ||
renderParams :: f a -> [Action] | ||
|
||
-- This instance might not make sense, though the signature of | ||
-- 'renderParams' sort of implies that it does since it returns a | ||
-- '[]''s in stead of a 'NonEmpty''s. | ||
instance QueryParams U1 where | ||
renderParams = const mempty | ||
|
||
instance Param a => QueryParams (K1 i a) where | ||
renderParams (K1 a) = pure $ render a | ||
|
||
instance QueryParams a => QueryParams (M1 i c a) where | ||
renderParams (M1 a) = renderParams a | ||
|
||
instance (QueryParams a, QueryParams b) => QueryParams (a :*: b) where | ||
renderParams (a :*: b) = renderParams a <> renderParams b |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{-# LANGUAGE ScopedTypeVariables, TypeOperators, InstanceSigs, KindSignatures #-} | ||
module Database.MySQL.Simple.QueryResults.Generic | ||
( QueryResults(..) | ||
) where | ||
|
||
import Prelude | ||
import Data.Kind | ||
import Data.ByteString (ByteString) | ||
import GHC.Generics | ||
|
||
import Database.MySQL.Base.Types (Field) | ||
import Database.MySQL.Simple.Result (Result(convert), convertError) | ||
|
||
class QueryResults (f :: Type -> Type) where | ||
convertResults :: [Field] -> [Maybe ByteString] -> (f a) | ||
|
||
-- This instance might not make sense, though the signature of | ||
-- 'convertResults' sort of implies that it does since it takes in two | ||
-- '[]''s in stead of two 'NonEmpty''s. | ||
instance QueryResults U1 where | ||
convertResults xs ys = case zip xs ys of | ||
[] -> U1 | ||
_ -> convertError xs ys 0 | ||
|
||
instance Result a => QueryResults (K1 i a) where | ||
convertResults xs ys = case zip xs ys of | ||
[(x, y)] -> K1 $ convert x y | ||
_ -> convertError xs ys 1 | ||
|
||
instance QueryResults a => QueryResults (M1 i c a) where | ||
convertResults xs ys = | ||
M1 $ convertResults xs ys | ||
|
||
instance (QueryResults a, QueryResults b) => QueryResults (a :*: b) where | ||
convertResults :: forall x . [Field] -> [Maybe ByteString] -> (:*:) a b x | ||
convertResults (x:xs) (y:ys) = | ||
-- I'm concerned about this implementation since it's biased | ||
-- towards one side. Meaning that it's expecting the chain of | ||
-- ':*:' to lean to the right. As an example of the problem | ||
-- consider the following: | ||
-- | ||
-- > data T = T Int Int Int deriving (Generic) | ||
-- > from (T 1 2 3) | ||
-- M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: (M1 {unM1 = K1 {unK1 = 2}} :*: M1 {unM1 = K1 {unK1 = 3}})}} | ||
-- | ||
-- If the result in stead had been like this (note the re-bracketing): | ||
-- | ||
-- M1 {unM1 = (M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = 2}}}) :*: M1 {unM1 = K1 {unK1 = 3}}} | ||
-- | ||
-- Then the generic derivation for 'T' would fail. | ||
convertResults [x] [y] :*: convertResults xs ys | ||
convertResults xs ys = convertError xs ys c | ||
where | ||
-- My guess is that we'd need @Typeable (a :*: b)@ to determine | ||
-- the number of constructors we're expecting here. | ||
c :: Int | ||
c = -1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters