haskell - 来自 generics-sop 的超类
问题描述
我正在尝试使用generics-sop 中的All来约束类型列表。使用简单的类,一切都按预期工作All Typeable xs
,但我希望能够执行以下操作:
class (Typeable a) => TestClass (a :: k)
instance (Typeable a) => TestClass a
foo :: (All Typeable xs) => NP f xs -> z
foo = undefined
bar :: (All TestClass xs) => NP f xs -> z
bar = foo
这给出了错误
Could not deduce: Generics.SOP.Constraint.AllF Typeable xs
arising from a use of ‘foo’
from the context: All TestClass xs
generics-sop 文档指出
"All Eq '[ Int, Bool, Char ] 等价于约束 (Eq Int, Eq Bool, Eq Char)
但在这种情况下,它似乎不是,因为
foo2 :: (Typeable a, Typeable b) => NP f '[a,b] -> z
foo2 = undefined
bar2 :: (TestClass a, TestClass b) => NP f '[a,b] -> z
bar2 = foo2
编译得很好。
我的问题
1)这是预期的行为吗?2)如果是这样,有什么解决方法吗?
我的用例是我想传递一个类型级别的类型列表,这些类型受单个类名(如)下的一堆不同类的约束,class (Typeable a, Eq a, Show a) => MyClass a
但也能够调用只需要这些类的某些子集的不太专业的函数.
不考虑寻找超类的答案,但我认为这不是问题所在 - 我认为这与All
约束的组合方式有关generics-sop
。就好像编译器只是比较两个All
约束,而不是扩展它们然后进行类型检查。
解决方案
All f xs
实际上等价于(AllF f xs, SListI xs)
。AllF
是一个类型族:
type family AllF (c :: k -> Constraint) (xs :: [k]) :: Constraint where
AllF _ '[] = ()
AllF c (x:xs) = (c x, All c xs)
xs
您会看到除非在 WHNF 中,否则它无法减少,因此它会卡在您的情况下。您可以使用mapAll
:
import Generics.SOP.Dict
mapAll :: forall c d xs.
(forall a. Dict c a -> Dict d a) ->
Dict (All c) xs -> Dict (All d) xs
-- ::ish forall f g xs. (forall a. f a -> g a) -> All f xs -> All g xs
-- stores a constraint in a manipulatable way
data Dict (f :: k -> Constraint) (a :: k) where
Dict :: f a => Dict f a
bar :: forall xs f z. (All TestClass xs) => NP f xs -> z
bar = case mapAll @TestClass @Typeable @xs (\Dict -> Dict) Dict of
Dict -> foo
-- TestClass a -> Typeable a pretty trivially:
-- match Dict to reveal TestClass a
-- put the Typeable part of the TestClass instance into another Dict
-- We already know All TestClass xs; place that into a Dict
-- mapAll magic makes a Dict (All Typeable) xs
-- match on it to reveal
-- foo's constraint is satisfied
推荐阅读
- ios - 如何知道我上传到 firebase 的文件是否已完成上传?
- intellij-idea - 生成自定义设置器 lombok 或 intelliJ
- python - 熊猫同时进行多个变换-分组聚合-加速
- bash - 作为服务运行时 Python 脚本未启动 Bash 脚本
- html - 调整为移动设备堆叠的列的高度 (@media)
- google-apps-script - Google 脚本在 gmail 中创建新的页面元素
- php - 是否可以用今天的日期自动填充 Wordpress 标题字段?
- javascript - 与 Promise 并行运行两个 javascript 函数
- swift - Swift – 在 MKCircle 中设置多个填充颜色
- ionic4 - Ionic 4 - 详细信息页面删除标签栏