Reputation:
I am using the following types to represent a user:
data CredentialsRep uType pType = Credentials { username :: uType
, password :: pType } deriving (Show)
data NameRep fType lType = Name { first :: fType
, last :: lType } deriving (Show)
data UserRep cType nType = User { credentials :: cType
, name :: nType} deriving (Show)
How can I write the FromRow
instance for UserRep
so that postgresql-simple
can turn my queries into useful types? So far, I have this:
instance FromRow (UserRep a b) where
fromRow = do
username <- field
password <- field
fname <- field
lname <- field
return $ (User (Credentials username password) (Name fname lname))
Which results in this error:
src/PSQLTEST.hs:30:5:
Couldn't match type ‘a’ with ‘CredentialsRep uType0 pType0’
‘a’ is a rigid type variable bound by
the instance declaration at src/PSQLTEST.hs:24:10
Expected type: RowParser (UserRep a b)
Actual type: RowParser
(UserRep (CredentialsRep uType0 pType0) (NameRep fType0 lType0))
Relevant bindings include
password :: pType0 (bound at src/PSQLTEST.hs:27:5)
username :: uType0 (bound at src/PSQLTEST.hs:26:5)
fromRow :: RowParser (UserRep a b) (bound at src/PSQLTEST.hs:25:3)
In a stmt of a 'do' block:
return $ (User (Credentials username password) (Name fname lname))
In the expression:
do { username <- field;
password <- field;
fname <- field;
lname <- field;
.... }
In an equation for ‘fromRow’:
fromRow
= do { username <- field;
password <- field;
fname <- field;
.... }
src/PSQLTEST.hs:30:5:
Couldn't match type ‘b’ with ‘NameRep fType0 lType0’
‘b’ is a rigid type variable bound by
the instance declaration at src/PSQLTEST.hs:24:10
Expected type: RowParser (UserRep a b)
Actual type: RowParser
(UserRep (CredentialsRep uType0 pType0) (NameRep fType0 lType0))
Relevant bindings include
lname :: lType0 (bound at src/PSQLTEST.hs:29:5)
fname :: fType0 (bound at src/PSQLTEST.hs:28:5)
fromRow :: RowParser (UserRep a b) (bound at src/PSQLTEST.hs:25:3)
In a stmt of a 'do' block:
return $ (User (Credentials username password) (Name fname lname))
In the expression:
do { username <- field;
password <- field;
fname <- field;
lname <- field;
.... }
In an equation for ‘fromRow’:
fromRow
= do { username <- field;
password <- field;
fname <- field;
.... }
Upvotes: 3
Views: 552
Reputation: 54078
(Disclaimer: I haven't used this library at all)
You're trying to define FromRow
for all UserRep a b
, but in the definition of fromRow
you're explicitly constructing a UserRep (CredentialsRep u p) (NameRep f l)
, hence the error saying it can't match a
with CredentialsRep u p
and b
with NameRep f l
(modulo type variable names). Instead, I believe you can do something like
instance (FromField u, FromField p, FromField f, FromField l) => FromRow (UserRep (CredentialsRep u p) (NameRep f l)) where
fromRow = do
username <- field
password <- field
firstname <- field
lastname <- field
return $ User (Credentials username password) (Name firstname lastname)
Or just
instance FromRow (UserRep (CredentialsRep String String) (NameRep String String)) where
...
with the same fromRow
definition. With this approach you may have to enable the FlexibleInstances
extension.
In addition, it looks like you may be making your types too polymorphic (but your situation might be unique, take this advice with a grain of salt), you could just get rid of the type parameters altogether and have
data CredentialsRep = Credentials
{ username :: String
, password :: String
} deriving (Show)
data NameRep = Name
{ first :: String
, last :: String
} deriving (Show)
data UserRep = User
{ credentials :: CredentialsRep
, name :: NameRep
} deriving (Show)
In which case you would just need
instance FromRow UserRep where
...
with the same fromRow
definition.
Upvotes: 5