我只是抬头查看了HasResolution
typeclass,它有一个方法,resolution
声明如下:
class HasResolution a where
...
resolution :: p a -> Integer
我不明白上述声明中的p
。它来自哪里,它意味着什么?
这只是一个代理人。
如果你有
class HasResolution a where
resolution :: Integer
你会被大吼大叫,因为当你调用HasResolution
时,编译器无法推断出你需要哪个resolution
实例。具体来说,resolution :: HasResolution a => Integer
,a
出现在左边但不在右边,所以你永远不能推断a
。
所以,一个解决方案是
class HasResolution a where
resolution :: a -> Integer
和resolution
的文件会说它不是要检查a
;它只是让编译器找出要选择的实例。你将它用作resolution (undefined :: a)
。然后,另一个解决方案出现
data Proxy a = Proxy
class HasResolution a where
resolution :: Proxy a -> Integer
Proxy
没有向resolution
提供任何信息;它只存在于编译器推断a
是什么。它比原来更好resolution
真的无法检查它的论点,所以Integer
真的与类型有关,而不是resolution
的论证。它的情况稍差(或更好,取决于你问的是谁),因为使用的是更冗长的resolution (Proxy :: Proxy a)
(你不能只使用undefined
因为实现可能在Proxy
上模式匹配)。
这演变成了
class HasResolution a where
resolution :: p a -> Integer
这意味着你没有被限制在Proxy
,这意味着如果你有例如位于范围内的[a]
,你可以将它传递给resolution
而不会产生大量的冗长,同时保持与刚刚使用Proxy
的代码的兼容性。同样,resolution
的第一个参数仅供编译器使用。它对实际实现没有任何意义。您只需使用它来选择所需的HasResolution
实例。
Proxy
最终变得如此普遍,以至于GHC.Exts
获得了新成员:Proxy#
。 Proxy#
没有运行时表示,因此它不会导致性能损失,这与Proxy
(或上述多态p
技巧)不同。然而,Proxy#
的事实是它的类型是forall k. k -> TYPE (TupleRep '[])
。如果没有像所有其他“乖巧”的类型一样生活在*
,它就不能参与多态的p
技巧。
class HasResolution a where
resolution :: Proxy# a -> Integer
第一个解决方案相当过时,尽管有时会看到示例。第二个和第三个相当普遍,第四个被最近的解决方案很快取代,这是为了启用-XTypeApplications -XAllowAmbiguousTypes
而且只是
class HasResolution a where
resolution :: Integer
再次。 -XAllowAmbiguousTypes
避开错误,-XTypeApplications
允许你在呼叫站点指定a
为resolution @a
。这不能用在需要一定程度向后兼容的代码中,但是在需要新GHC的库中你会看到更多,并且可以承受不具备兼容性。