Given the following data type:
data FormInputH f = FormInputH
{ formNameH :: f Text,
formEmailH :: f Text,
formAgeH :: f Int
}
deriving stock (Generic)
deriving anyclass (ApplicativeB, ConstraintsB, FunctorB, TraversableB)
(ApplicativeB etc. come from the barbies library)
I'd like to write an instance for the FromForm type class from http-api-data. It's relatively straightforward to write an instance directly for all f that have an Applicative instance:
instance (Applicative f) => FromForm (FormInputH f) where
fromForm f = FormInputH
<$> (pure <$> parseUnique "name" f)
<*> (pure <$> parseUnique "email" f)
<*> (pure <$> parseUnique "age" f)
What I'd like to achieve is an equivalent implementation that uses less duplication int he code. For that purpose, I created a value that defines the names of the fields in the form:
formFieldNames :: FormInputH (Const Text)
formFieldNames =
FormInputH
(Const "name")
(Const "email")
(Const "age")
so that I can write more generic code in the FromForm instance:
instance (Applicative f) => FromForm (FormInputH f) where
fromForm f = btraverse (\(Const name) -> pure <$> parseUnique name f) formFieldNames
Which doesn't work because
• Could not deduce (FromHttpApiData a)
arising from a use of ‘parseUnique’
from the context: Applicative f
bound by the instance declaration at src/FormInput.hs:41:10-51
Possible fix:
add (FromHttpApiData a) to the context of
a type expected by the context:
forall a. Const Text a -> Either Text (f a)
Is it possible to fix that? I kinda feel it's not, mainly because the forall a. in the signature of btraverse seems to not allow this flexibility here.
I tried adding AllBF constraint, but that didn't help
You can use
baddDicts(from the classConstraintsH).The function argument of
btraversehas typeforall a. f a -> e (g a), wheref ais the type of each field. You need aFromHttpApiData aconstraint, which can only come fromf, and this is indeed possible by choosingfto beProduct (Dict FromHttpApiData) (Const Text), which you can obtain frombaddDicts(provided each field of the record does implementFromHttpApiData).Minimal compilable snippet: