haskell - 我可以根据某个类的可用性过滤类型级别列表吗?
问题描述
我可以使用这样的类型族将函数与空值分开:
type family Funs (ts :: [*]) :: [*]
where
Funs '[ ] = '[ ]
Funs ((a -> b): ts) = (a -> b): Funs ts
Funs (k: ts) = Funs ts
我想要的是分离满足约束的类型,例如Show
. 类比尝试:
type family Showable (ts :: [*]) :: [*]
where
Showable '[ ] = '[ ]
Showable ((Show a => a): ts) = a: Showable ts
Showable (k: ts) = Showable ts
— 导致错误:
• Illegal qualified type: Show a => a
• In the equations for closed type family ‘Showable’
In the type family declaration for ‘Showable’
|
35 | Showable ((Show a => a): ts) = a: Showable ts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
可以做什么?我对使用 Template Haskell 或任何类型的低级黑客的解决方案很好。
解决方案
由于开放世界的假设,我不相信可以轻松地做到这一点(没有 TH):GHC 基本上永远不会解决类约束的负面影响,因为在某处可能会有更多的实例使它成为现实(并且不要很好地使用 GHC/Haskell 使用的单独编译策略)。因此,通常不可能——从纯粹的“常规” Haskell 代码中——决定一个类型是否具有类实例,以及是否将其包含在列表中。
如果您愿意稍微破坏单独的编译,只考虑编译您正在处理的模块时范围内的实例(即在该模块的源文件中的范围内),您可以使用 Template Haskell 或 GHC 类型检查器插件得到非常像这种行为的东西。我知道有几个实现在价值级别做类似的事情,包括ifcxt和constraints-emerge。我相信这些库,尤其是 ifcxt(我稍微熟悉一点)非常简单:您可以使用 TH 函数reify
来获取ClassI
Info
特定类型类的 a,并使用它的[InstanceDec]
字段以获取编译期间范围内的所有实例的列表。然后,您基本上可以为每个将实例头添加到列表中的具体类型实例创建一个分支,然后用一个不会的包罗万象的分支跟进它。您可能还需要递归地执行此操作以处理本身具有约束的实例。
请注意,如果您选择使用这种方法,这将以可能令人困惑的方式打破开放世界假设:如果模块导入类型级过滤器模块,然后定义数据类型/实例,则类型级过滤器将不会知道新实例,并将继续将该类型视为没有实例。当您使用 TH 生成过滤器类型族时,您需要确保您关心的所有实例都在范围内。
如果您想在某种程度上改进这一点,您可以使用一种更像IfCxt
where 的方法,而不是直接创建类型族实例,您可以执行以下操作:
class IsShow (a :: Type) (b :: Bool) where
instance {-# OVERLAPPABLE #-} (b ~ 'False) => IsShow a b where
你有你的 TH 生成实例,如:
instance IsShow Int 'True where
这样做的好处是,如果另一个模块定义了重要的类型/实例,您应该能够(大致)使用相同的 TH 来扩展IsShow
这些新实例的实例,并且您使用的类型系列IsShow
应该没问题。上面链接的 ifcxt 包基本上做同样的事情,但它没有做必要的技巧来获取类型级别的信息,它只是生成函数来获取值级别的信息。
此解决方案使用具有功能依赖关系的类而不是类型族,因为OverlappingInstances
可以为基于类的解决方案提供“默认情况”。我不确定是否有任何合理的方法可以为开放类型系列提供默认情况,因此在到处使用类型系列(而不是 fundep'd 实例)时,您可能无法获得这种“可扩展性”。
理查德艾森伯格说
对于单独的编译,缺少排序和重叠检查对于类型的健全性是必要的。
所以我认为这可能是不可能的。这里也有一些关于类型族与基金的有趣讨论: https ://typesandkinds.wordpress.com/2015/09/09/what-are-type-families/
推荐阅读
- python-3.x - 我的 tk 脚本在我直接运行时有效(python 启动器在 root.quit 上退出)但在导入另一个脚本时不起作用(python 启动器冻结)
- arrays - 如何对数组中除特殊字符外的所有内容进行排序 - Ruby
- python-3.x - PYGAME:UnboundLocalError:分配前引用的局部变量
- javascript - 关于文本的两个数组的比较
- .net - 如何在 Navisworks API 中添加自定义冲突检测规则?
- laravel - 如何在 laravel 8 中保存多项选择?
- java - 无代码 Android BLE 特性始终返回相同的值
- javascript - 如何使用模板文字为按钮添加多行文本
- html - 针对容器而不是图像的 css 边距
- javascript - JavaScript - 迭代特定的嵌套数组