上下文中的约束如何更改Haskell中的实例解析

时间:2019-02-27 19:00:48

标签: haskell typeclass

我试图了解如何在实例上下文中添加约束来更改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

最后两行不是我想要的。对于第三行,似乎在为列表实例运行f时,编译器(或运行时?)不知道aInt,而仅使用默认的{ {1}}个实例。同样,对于最后一行,无法确定C a是另一个列表。但是,如果我向列表实例添加上下文:

a

输出变为:

instance {-# OVERLAPPING #-} (C a) => C [a] where
  f [] = "empty list"
  f (x : xs) = "list of " ++ f x

...这就是我想要的。在此示例中,有人可以帮助解释该约束如何改变实例分辨率吗?有什么我可以看的一般规则吗?

1 个答案:

答案 0 :(得分:5)

基本上,实例选择仅在编译时执行。在运行时,周围没有类型信息,也没有存储在任何位置来驱动实例选择的实例列表。

那么,您的实例中发生了什么?考虑第一种情况:

instance {-# OVERLAPPING #-} C [a] where
  f [] = "empty list"
  f (x : xs) = "list of " ++ f x

假设f被传递了[a0]类型的列表(为清楚起见,我们使用一个新鲜的变量名)。然后,我们需要在最后一行中输入check f x。上面的变量x的类型为a0,因此GHC需要求解C a0。为此,GHC必须选择一些实例。通常,它会拒绝选择instance C a,因为instance C Int也存在,并且GHC不知道是否a0 = Int,因此没有一个实例可以被选为“确定的”实例,仅包含以下信息:手。

但是,“重叠”实用指令指示GHC在足以解决约束的通用实例中选择单个最佳实例。确实,这是我们可以利用现有信息做的最好的事情:唯一的其他合理选择就是引发错误。

在这种情况下,要解决C a0,我们需要选择三个实例之一,并且一般只有instance C aC 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的{​​{1}})。

因此,现在GHC有两个可行的选择:使用a0上下文(即使用隐式参数)解决C a0或求助于全局C a0。第一个更为具体,因为它仅适用于instance C a而不适用于任何类型的a0,因此它被认为是“最佳”的,因此被选中。