haskell - 上下文中的约束如何改变 Haskell 中的实例解析
问题描述
我试图了解向实例上下文添加约束如何更改 Haskell 中的实例解析。在这个例子中:
class C a where
f :: a -> [Char]
instance {-# OVERLAPPABLE #-} C a where
f = const "thing"
instance C Int where
f = const "int"
instance {-# OVERLAPPING #-} C [a] where
f [] = "empty list"
f (x : xs) = "list of " ++ f x
main = do
putStrLn $ f (1 :: Int)
putStrLn $ f [(True :: Bool)]
putStrLn $ f [(1 :: Int)]
putStrLn $ f [[(1 :: Int)]]
输出是:
int
list of thing
list of thing
list of thing
最后两行不是我想要的。对于第 3 行,似乎编译器(或运行时?)在f
为列表实例运行时不知道那a
是Int
并且只使用默认C a
实例。同样对于最后一行,它无法确定这a
是另一个列表。但是,如果我向列表实例添加上下文:
instance {-# OVERLAPPING #-} (C a) => C [a] where
f [] = "empty list"
f (x : xs) = "list of " ++ f x
输出变为:
int
list of thing
list of int
list of list of int
...这就是我想要的。有人可以帮助解释该约束如何更改此示例中的实例分辨率吗?有什么一般规则我可以看看吗?
解决方案
本质上,实例选择仅在编译时执行。在运行时,周围没有类型信息,也没有存储在任何地方的实例列表来驱动实例选择。
那么,您的实例中发生了什么?考虑第一种情况:
instance {-# OVERLAPPING #-} C [a] where
f [] = "empty list"
f (x : xs) = "list of " ++ f x
假设f
传递了一个类型列表[a0]
(为了清楚起见,我们使用一个新的变量名)。然后我们需要f x
在最后一行输入 check 。上面的变量x
是有类型a0
的,所以GHC需要解决C a0
。为此,GHC 必须选择一些实例。通常它会拒绝选择,instance C a
因为它instance C Int
也存在,而 GHC 不知道是否存在a0 = Int
,因此只有手头的信息不能选择单个实例作为“最终”实例。
然而,“重叠”编译指示指示 GHC 在足以解决约束的通用实例中选择单个最佳实例。事实上,这是我们利用手头的信息所能做的最好的事情:唯一的其他合理选择是引发错误。
在这种情况下,要解决C a0
我们需要在三个实例中选择一个,并且只有instance C a
一般的足够匹配C a0
(毕竟,a0
可能是非Int
,非列表类型)。所以我们选择那个。
相反,使用
instance {-# OVERLAPPING #-} (C a) => C [a] where
f [] = "empty list"
f (x : xs) = "list of " ++ f x
打开第四个选项来解决C a0
,即使用C a0
可用的上下文。当f
被调用时,它被传递一个带有字典的隐式参数C a0
(即f
类型a0
)。
所以,现在 GHC 有两个可行的选择:C a0
使用C a0
上下文求解(即使用隐式参数),或者求助于全局instance C a
. 第一个更具体,因为它仅适用于a0
而不适用于任何类型a
,因此它被认为是“最好的”,并被选中。
推荐阅读
- android - 自定义工具栏
- java - 有没有按照我们想要的顺序显示两个不同类的两个对话框?
- three.js - 无法使用 THREE.js 和 Mapbox GL 投射阴影
- javascript - 如何通过 $POST 从 ajax 发送对象数据(从 jQuery 到 vanilla JS)
- laravel - 有没有办法将电话身份验证添加到默认的 laravel auth 脚手架
- c++ - 不能包含 cliext 标头(用于 c++ cli、Visual Studio)
- c# - 在新的自动增量后立即更新记录
- javascript - underscore.js 的油门实现中的“剩余 > 等待”条件语句何时为真?
- javascript - 从加载了客户端 JavaScript 的页面获取呈现的 HTML?
- c++ - 为什么我无法在 Qt Designer 中删除 tabWidget?