【问题标题】:typeclasses without methods, used as constraints: do they get dictionaries?没有方法的类型类,用作约束:他们有字典吗?
【发布时间】:2019-03-26 13:12:57
【问题描述】:

如果我使用类型类来重载方法,那是在“字典传递样式”中实现的。也就是说,该方法获得了一个额外的参数(没有出现在表面 Haskell 中);为了解决重载,该方法按其“正确”参数的类型查找字典;并从字典中提取方法实现。 As described in this q, for example.

但是没有方法的类型类呢?它们可以用作约束。有他们的字典吗?它包含什么?

举个具体的例子:

module M  where

class C a             -- no methods
                      -- no instances in module M
f :: C a => a -> Bool
f x = blah

此模块中没有C 的实例,所以如果M 被导入到其他模块(带有C 实例),f 的字典查找将如何编码?

通常的情况是类C 有方法;在f 等式的 RHS 上有电话给他们;所以想要的C a 被编码为方法调用的字典查找。

补充问题:(如果有人还在听)

2a。对于具有超类约束的无方法类型类:约束在字典中的什么位置? comment on a ticket from SPJ 似乎表明它是字典数据构造函数的参数。

2b。对于有约束的无方法类型类实例:同样,约束在字典中的什么位置?

动机

cmets 中的@Daniel 正在询问这些 q 的动机。除了更好地理解编译器内部......

GHC 将表面 Haskell 转换为内部表示:System FC,它在每个函数应用程序中都有明确的类型签名。 (这些应用程序必须包括应用类的字典。)我试图了解类和实例声明的所有类型相关组件在字典中的位置;弄清楚 FC 中的术语如何获得与原始 Haskell 等效的表示。然后我不明白 [没有方法的类型类] 是如何适应的。因为这些类不能直接作为表面 Haskell 中的术语出现,而只能作为约束出现。那么它必须是在术语级别表示它的此类的字典。

如果你想问这是怎么回事:FC 中似乎有一个限制,它不能表示函数依赖。与无法生成类型级证据有关。然后我想了解这种限制是如何产生的/FunDeps 不能(或目前没有)代表什么?

【问题讨论】:

  • 这里似乎有两个不同的问题。您不能调用f,因为没有C 的实例。但是如果你添加一个实例,比如instance C Char,那么f 'a' 可以正常工作。
  • 好的。我应该学会不要匆忙编辑 q。再次编辑。参考@Chepner 的评论,我不能打电话给f,但我可以导出它。我故意不想在这个模块中释放可释放的约束,所以我强制f 的调用者访问字典。
  • 感谢@DanielWagner 的回答。如果您还在听,我添加了一个补充 q。
  • “超类约束”和“约束”之间有什么区别(这两件事是我在您的问题 2a 和 2b 之间看到的唯一文本差异)?至于“他们去哪里”,我不确定这个问题是否明智。由于字典在表面语言中不明确可用,因此无法观察其中特定位的 哪里
  • 2b “带有约束的实例”表示对实例的约束。不,我们无法查看字典,但您在这里和链接中的 SPJ 都提供了示例。我认为这是 GHC 用来证明术语类型满足约束条件的机制,即使它没有调用方法。

标签: haskell typeclass


【解决方案1】:

有没有[没有方法的类型类]的字典吗?它包含什么?

是的,有一本没有字段的字典。

比较:

class Monoid a where
    mempty :: a
    mappend :: a -> a -> a
data DictMonoid a = DictMonoid a (a -> a -> a)

class C a
data DictC a = DictC

如果M 被导入到其他模块(带有C 实例),f 的字典查找是如何编码的?

类型推断用于确定调用f时需要什么实例;然后 GHC 在其已知实例集合(=已知字典)中查找该类型。

这个过程的一个可能结果是我们确定需要的实例是多态的,并且不存在完全多态的实例。然后将适当的约束(例如C aC m 或其他)附加到调用f 的任何术语的推断类型——然后将其编译为接受f 上的字典的函数代为传递。

对于具有超类约束的无方法类型类:约束在字典中的什么位置?

某处。您无法从表面语言中进行观察以区分不同的地方。例如,考虑:

class Semigroup a => Monoid a where mempty :: a
data DictMonoid1 a = DictMonoid1 (DictSemigroup a) a
data DictMonoid2 a = DictMonoid2 a (DictSemigroup a)

对于放置超类字典的位置,这是仅有的两个选择。但它可能有什么不同呢?

好的,但是您询问了无方法类型类。但答案是一样的。你无法分辨超类字典的存储顺序。

class (A a, B a) => C a
data DictC1 a = DictC1 (DictA a) (DictB a)
data DictC2 a = DictC2 (DictB a) (DictA a)

你能做些什么来区分这些?什么都没有。

对于具有约束的无方法类型类实例:同样,约束在字典中的什么位置?

无处可去。它们成为调用者必须提供以接收字典的参数。当然,提供的字典的特定字段可能会被新字典关闭。示例:

class Ord a where compare :: a -> a -> Ordering
data DictOrd a where DictOrd (a -> a -> Ordering)

instance (Ord a, Ord b) => Ord (a, b) where
    compare (a,b) (a',b') = compare a a' <> compare b b'
instanceOrdTuple :: DictOrd a -> DictOrd b -> DictOrd (a,b)
instanceOrdTuple (DictOrd comparea) (DictOrd compareb)
    = DictOrd $ \(a,b) (a',b') -> comparea a a' <> compareb b b'

好的,但是您询问了无方法类型类。但答案并没有明显不同。实例的约束字典不存储在任何地方,就像以前一样;唯一不同的是,现在我们还可以确保即使提供的字典的字段也没有关闭。

class A a where whateverA :: a -> Int
class B a where whateverB :: Int -> a
class C a
data DictA a = DictA (a -> Int)
data DictB a = DictB (Int -> a)
data DictC a = DictC

instance (A a, B a) => C [a]
instanceCList :: DictA a -> DictB a -> DictC [a]
instanceCList (DictA whateverAa) (DictB whateverBa) = DictC

以下评论回复了旧版本的问题。

此模块中没有C 的实例,因此编译器无法解除fs 约束。

不需要。 f 被编译成一个函数,接受一个字典作为参数;不需要创建字典来编译f,只需要编译它的调用者。

编译器无法解除由y 的等式引起的C Char 约束。它有什么作用?

它报告它无法解除C Char 约束并且未成功退出。 (这甚至算不上一个问题——你自己试试看吧!)

【讨论】:

  • "你无法分辨超类字典的存储顺序。"确实。我不是在问这个。从答案的上一版本(“没有字段的字典”)来看,尚不清楚约束字典是存储在与方法重载相同的字典中,还是存储在其他结构中。然后它是一个包含约束字段(如果有)但没有方法字段(因为没有)的字典。
  • @AntC 啊,是的。字典包含方法和超类字典作为字段。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-28
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多