Further to my goal of reducing the boilerplate required to use Haxl with a relational database, I am trying to package up the result of a raw SQL request via Persistent in an existentially quantified type. However the type checker won't allow it:
data SomeRawSql where
SomeRawSql :: forall b. RawSql b => [b] -> SomeRawSql
packedVal = let res = runDB $ rawSql "SELECT * FROM ..." [toPersistValue (pack "ABC")]
in fmap SomeRawSql res
This results in a type error on the line with fmap: Ambiguous type variable ‘b0’ arising from a use of ‘SomeRawSql’ prevents the constraint ‘(RawSql b0)’ from being solved.
The type of rawSql from persistent is:
rawSql :: (RawSql a, MonadIO m)
=> Text -- ^ SQL statement, possibly with placeholders.
-> [PersistValue] -- ^ Values to fill the placeholders.
-> ReaderT SqlBackend m [a]
runDB
is a helper function that connections to the database and returns IO [a]
. Based on the definition of rawSql I would expect the RawSql constraint to be satisfied. I don't understand why this error arises.
rawSql
is universally quantified. That means, it does not “extract aRawSql
instance from the database”, which would be what the existential typeSomeRawSql
expresses. Instead it can extract values from the database provided they have aRawSql
instance. What type this is is chosen by the caller.You could also wrap the universal quantification in a parameterless type:
but I don't think that would be sensible, it just kicks the burden of choosing a type down the road. Parametricity is a good thing, it allows you to actually keep track of what types are going where. Don't circumvent it without a real reason!
An entirely different subject is if you want to retrieve a value whose type you really do not know. That's not covered by
rawSql
, you'd need to implement it yourself with wrappers likeDynamic
.