Haskell使用singletons包时为已有类型添加Typeclass约束的方案咨询
解决方案
你不需要重定义singletons库的核心类型与函数,只需要将「所有SType对应的类型都具备Typeable实例」这一事实告知编译器即可,有两种常用实现方式:
方式1:无额外依赖实现
直接写一个高阶辅助函数封装约束传递逻辑:
-- 给SType添加约束携带辅助函数 withSTypeTypeable :: SType a -> (Typeable a => r) -> r withSTypeTypeable SInteger f = f withSTypeTypeable SBoolean f = f withSTypeTypeable SByteStr f = f -- 修改checkResult实现,调用辅助函数引入Typeable约束 checkResult :: EType -> UntypedExp -> Maybe Result checkResult typ expr = withSomeSing typ $ \singType -> withSTypeTypeable singType $ makeResult singType <$> inferExpr expr
每个分支匹配到具体的SType构造器时,对应的a类型是确定的,Typeable实例天然存在,编译器会自动识别该约束并允许调用inferExpr。
方式2:使用constraints包简化写法
如果你的项目已经依赖了constraints包,可以用Dict更简洁地实现约束传递:
import Data.Constraint (Dict(..)) sTypeHasTypeable :: SType a -> Dict (Typeable a) sTypeHasTypeable SInteger = Dict sTypeHasTypeable SBoolean = Dict sTypeHasTypeable SByteStr = Dict checkResult :: EType -> UntypedExp -> Maybe Result checkResult typ expr = withSomeSing typ $ \singType -> case sTypeHasTypeable singType of Dict -> makeResult singType <$> inferExpr expr
模式匹配到Dict时,Typeable a约束会自动进入作用域,后续调用inferExpr就不会报错。
方案优势
对比你当前自定义SomeSing、SingKind的临时方案,该方案有几个明显好处:
- 不需要隐藏
singletons库的核心定义,后续库版本升级不会出现兼容问题 - 不需要修改
SingKind的泛用实现,仅针对你自定义的SType添加少量辅助代码即可 - 逻辑更清晰,明确限定只有
SType对应的类型才携带Typeable约束,不会破坏singletons原有功能的泛用性
内容的提问来源于stack exchange,提问作者Jack Ek
相关产品推荐
相关产品推荐

