【发布时间】:2012-02-09 23:51:58
【问题描述】:
在过去的几个月里,我一直在努力学习 Haskell - 以前,我是一个看似永远的新手,对基础知识的了解非常有限。在尝试将我的新知识应用到从 hello-world 项目开始的几个步骤中时,我不断发现我想使用基于类型类的类似代理的模式。前几次,当我弄清楚它为什么不起作用时,我将其驳回为“好的——我可能找不到一个惯用的 Haskell 替代品,但这里的问题很可能是我使用了错误的方法语言”。但我发现我真的真的不喜欢不能做类似代理的事情。
试图更深入地了解为什么我不能使用代理,经过大量试验后,我终于想通 GHC 更高级别的类型扩展,也许我可以拥有代理。但我仍然不能让它工作,我不知道为什么。
这是我管理过的最好的代码...
{-# LANGUAGE RankNTypes #-}
module Test where
-- Simple type class based on parser combinators.
class Gen g where
get :: g x -> [(x, g x)]
instance Gen [] where
get [] = []
get (x:xs) = [(x, xs)]
-- Proxy type - holds a pair containing...
-- - a value of some type that supports Gen
-- - a function to indicate when an item should be skipped
newtype PROXY nestedgen x = Proxy (nestedgen x, x -> Bool)
proxyskip :: Gen nestedgen => PROXY nestedgen r -> Bool
proxyskip (Proxy (g, predf)) = case get g of
[] -> False
((r,_):_) -> predf r
proxyget :: Gen nestedgen => PROXY nestedgen r -> [(r, PROXY nestedgen r)]
proxyget pr@(Proxy (sg, predf)) = if proxyskip pr
then [(r2, g2) | (_, g1) <- get sg, (r2,g2) <- proxyget (Proxy (g1, predf))]
else [(r3, Proxy (g3, predf)) | (r3,g3) <- get sg]
-- Instance of Gen for PROXY - get skips items where appropriate
instance Gen nestedgen => Gen (PROXY nestedgen) where
get = proxyget
-- Test "parser"
-- Get the specified number of items, providing them as a list (within
-- the list of nondeterministic (result, state) pairs).
getN :: Gen g => Int -> g x -> [([x], g x)]
getN n g | (n < 0) = error "negative n"
| (n == 0) = [([], g)]
| True = [(r1:r2, g2) | (r1, g1) <- get g, (r2, g2) <- getN (n-1) g1]
-- Wrap some arbitrary "parser" in a PROXY that skips over the letter 'l'
proxyNotL :: Gen gb => gb Char -> PROXY gb Char
proxyNotL gg = (Proxy (gg, \ch -> (ch /= 'l')))
call_f0 :: Gen gb => (Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, PROXY gb Char)]
call_f0 f0 g0 = f0 (proxyNotL g0)
test :: Gen gb => (Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, gb Char)]
test f0 g0 = [(r, g2) | (r, Proxy (g2, _)) <- call_f0 f0 g0]
最后剩下的错误发生在call_f0 f0 g0 = f0 (proxyNotL g0)...行上。
[1 of 1] Compiling Test ( Test.hs, Test.o )
Test.hs:44:21:
Could not deduce (ga ~ PROXY gb)
from the context (Gen gb)
bound by the type signature for
call_f0 :: Gen gb =>
(Gen ga => ga Char -> [(r, ga Char)])
-> gb Char
-> [(r, PROXY gb Char)]
at Test.hs:44:1-33
`ga' is a rigid type variable bound by
the type signature for
call_f0 :: Gen gb =>
(Gen ga => ga Char -> [(r, ga Char)])
-> gb Char
-> [(r, PROXY gb Char)]
at Test.hs:44:1
Expected type: ga Char
Actual type: PROXY gb Char
In the return type of a call of `proxyNotL'
In the first argument of `f0', namely `(proxyNotL g0)'
In the expression: f0 (proxyNotL g0)
查看有问题的函数...
call_f0 :: Gen gb => (Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, PROXY gb Char)]
call_f0 f0 g0 = f0 (proxyNotL g0)
f0 函数是(如果我正确理解更高级别的类型)作为参数传递的多态函数,类型为Gen ga => ga Char -> [(r, ga Char)]。在翻译成 C 的术语中,调用者传入了一个函数指针,但没有提供 vtable 指针。
proxyNotL 函数返回PROXY gb Char 类型的东西,并且有一个实例声明instance Gen nestedgen => Gen (PROXY nestedgen) where ...,因此PROXY gb Char 实例Gen 提供gb 实例Gen,它根据类型执行call_f0的签名。
基本上,据我所知,GHC 应该说“我可以提供f0 需要的vtable...嗯...是的,因为PROXY gb 实例Gen,我知道@987654338 @ 和 gb,是的,我可以”。
那么...为什么 GHC 拒绝统一 ga 和 Proxy gb?为什么 GHC 拒绝调用多态函数,其参数值应该由该函数的多态类型支持?
或者,我在这里完全误解了什么?
【问题讨论】: