使用Barbies库处理高种类数据类型的FromForm实例推导问题
解决Barbie类型的FromForm无重复实现问题
问题背景
我们有一个基于Barbies库定义的泛型数据类型:
data FormInputH f = FormInputH { formNameH :: f Text, formEmailH :: f Text, formAgeH :: f Int } deriving stock (Generic) deriving anyclass (ApplicativeB, ConstraintsB, FunctorB, TraversableB)
想要为它实现http-api-data库的FromForm实例,手动编写的无重复版本尝试使用预定义字段名和btraverse,但编译失败:
预定义字段名:
formFieldNames :: FormInputH (Const Text) formFieldNames = FormInputH (Const "name") (Const "email") (Const "age")
失败的实例代码:
instance (Applicative f) => FromForm (FormInputH f) where fromForm f = btraverse (\(Const name) -> pure <$> parseUnique name f) formFieldNames
编译错误:
• 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)
错误原因
btraverse要求遍历函数对**所有类型a**都能生效,但parseUnique需要FromHttpApiData a的约束。虽然我们知道FormInputH的字段类型(Text、Int)都满足该约束,但编译器无法自动推导这一点——它看不到a的具体类型,无法确认约束成立。
解决方案
利用Barbies库的ConstraintsB提供的btraverseWithConstraint函数,结合AllBF约束来传递类型约束:
import Data.Constraint (Dict(..)) import Barbies.Constraints (AllBF, btraverseWithConstraint) instance (Applicative f, AllBF FromHttpApiData FormInputH) => FromForm (FormInputH f) where fromForm form = btraverseWithConstraint @FromHttpApiData (\Dict (Const name) -> pure <$> parseUnique name form) formFieldNames
代码解释
AllBF FromHttpApiData FormInputH约束:明确告诉编译器,FormInputH的所有字段类型(Text、Int)都实现了FromHttpApiData类型类。btraverseWithConstraint函数:这是ConstraintsB提供的遍历函数,它能在遍历每个字段时,自动获取对应类型的约束字典(Dict FromHttpApiData a),证明当前字段的类型满足FromHttpApiData约束。- 遍历函数:我们忽略
Dict(因为约束已经保证它存在),用预定义的字段名调用parseUnique,再通过pure把解析结果包装成f a类型,最终组合成FormInputH f实例。
这个实现和手动编写的版本完全等效,同时消除了代码重复。
内容的提问来源于stack exchange,提问作者l7r7
相关产品推荐
相关产品推荐

