如何为带Typeable约束的自定义GADT实现外部库Arrow类型类实例
为带Typeable约束的自定义Arrow类型实现标准Arrow实例的方案
核心矛盾在于标准
Arrow类的arr方法签名要求支持任意无约束的a/b类型,而你的GADT构造要求a/b必须携带Typeable约束。针对Typeable这个特定约束,有完全兼容标准Arrow接口的安全实现方案;如果是其他非全量覆盖的约束,也有可落地的折中方案。
方案1:利用Typeable全类型覆盖特性实现安全实例
从GHC 7.8版本开始,所有属于Type种类的类型都会自动生成Typeable实例,不存在例外情况。因此我们可以安全地构造Typeable约束的证明,不需要依赖外部传入:
{-# LANGUAGE GADTs #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE TypeApplications #-} import Control.Arrow (Arrow(..)) import Data.Typeable (Typeable) import Data.Constraint (Dict(..)) import Unsafe.Coerce (unsafeCoerce) data MyArr a b where Arr :: (Typeable a, Typeable b) => (a -> b) -> MyArr a b -- 安全构造Typeable约束证明:所有Type种类的类型都有Typeable实例 typeableDict :: forall a. Dict (Typeable a) typeableDict = unsafeCoerce (Dict :: Dict ()) instance Arrow MyArr where arr f = case (typeableDict @a, typeableDict @b) of (Dict, Dict) -> Arr f -- 其余Arrow方法可按照普通GADT模式匹配实现 (Arr f) >>> (Arr g) = Arr (g . f) first (Arr f) = Arr \(a, c) -> (f a, c)
方案优势:
- 完全兼容标准
Arrow接口,可直接使用proc notation和所有第三方Arrow生态库 - 约束检查完全在编译期完成,无运行时开销
- 用到的
unsafeCoerce是100%安全的,不会触发任何运行时错误
方案2:通用约束场景的折中实现
如果你未来需要替换为非全量覆盖的约束(比如自定义的类,只有部分类型有实例),无法保证所有a/b都满足约束,可以选择两种折中路径:
- 延迟约束检查到运行时:在
arr方法中通过Data.Typeable或约束反射库检查约束是否满足,不满足则抛出明确的运行时错误,这种方案依然可以兼容标准Arrow接口,只需要在文档中明确说明支持的类型范围即可。 - 自定义Arrow类+语法扩展:如果完全不能接受运行时错误,可以使用你提到的带关联约束类的自定义Arrow定义,配合
arrows等QuasiQuoter工具,替换Haskell原生proc notation的arr调用,实现编译期约束检查。
内容的提问来源于stack exchange,提问作者Josh.F
相关产品推荐
相关产品推荐

