如何为Haskell异构集合的所有元素应用统一类型约束并实现映射
1 报错原因解释
- 第一种实现的报错:
你写的普通函数签名仅声明了AllConstrained c types约束,但GHC无法仅凭类型族约束自动对类型级列表types做递归分发处理,要遍历异构列表必须依赖类型类实例的模式匹配。另外你签名里的约束c和高阶函数参数内部forall量化的约束c属于不同作用域,GHC无法判定二者是同一个变量,因此会生成未确定的类型变量c0抛出无法推导的错误。 - 第二种实现的报错:
你把AllConstrained c types约束放到了类型类方法的签名里,而不是实例上下文中,属于多余声明。且直接传入ContraintRunnerF show时,约束参数c没有被显式指定,GHC的类型推断不会反向匹配后续HTypeMap元素的约束来确定c的具体值,因此会抛出c0不确定的错误。
2 通用映射函数实现
首先你需要开启以下GHC扩展:
{-# LANGUAGE GADTs #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE RankNTypes #-}
你不需要额外的ContraintRunnerF包装,直接把约束放到类型类实例的上下文即可:
import Prelude hiding (map) data HTypeMap (f :: * -> *) (types :: [*]) where HNil :: HTypeMap f '[] HCons :: f e -> HTypeMap f types -> HTypeMap f (e ': types) class MapConstrained (c :: * -> Constraint) (types :: [*]) where mapConstrained :: forall f r. (forall t. c t => f t -> r) -> HTypeMap f types -> [r] instance MapConstrained c '[] where mapConstrained _ HNil = [] instance (c t, MapConstrained c ts) => MapConstrained c (t ': ts) where mapConstrained f (HCons e rest) = f e : mapConstrained @c f rest
你期望的showEverything可以直接实现:
showEverything :: MapConstrained Show types => HTypeMap f types -> [String] showEverything = mapConstrained @Show show
测试用例可以直接运行,不需要显式包装高阶参数:
testMap :: HTypeMap Maybe '[Int, String] testMap = HCons (Just 3) (HCons (Just "Hello") HNil) test2 :: [String] test2 = mapConstrained @Show show testMap -- 运行结果: ["Just 3","Just \"Hello\""]
如果要兼容你最开始定义的AllConstrained类型族,只需要加一行等价约束映射即可。
内容的提问来源于stack exchange,提问作者Valentin Golev
相关产品推荐
相关产品推荐

