在过去的几个月里,我一直在努力学习Haskell - 之前,我是一个看似永恒的新手,对基础知识知之甚少。在试图将我的新知识付诸实践时,我将继续发现我希望使用基于类型类的类似代理的模式。前两次,当我弄清楚它为什么不起作用时,我认为它“好吧 - 我可能找不到一个惯用的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
,我知道PROXY
和gb
,是的,我可以“。
那么......为什么GHC拒绝统一ga
和Proxy gb
?为什么GHC拒绝调用多态函数,其参数值应该由该函数的多态类型支持?
或者,我在这里完全误解了什么?
答案 0 :(得分:5)
您只需要通过将通用量词f0
添加到类型签名中,明确指定函数forall ga.
必须是多态的:
call_f0 :: Gen gb => (forall ga. Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, PROXY gb Char)]
test :: Gen gb => (forall ga. Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, gb Char)]
否则,GHC将在最外层放置一个隐式forall ga.
,这意味着调用者将决定应该使用哪个ga
,而GHC已正确推断出这些函数必须能够选择自己ga
应为PROXY gb
。
换句话说,当使用RankNTypes
来要求多态参数时,必须始终使用显式量词来指定它。
答案 1 :(得分:1)
在这种情况下,我不太确定“代理”是什么意思。但我收集你可能表示表示Gen
。
采用Gen
类型类
class Gen g where
get :: g x -> [(x, g x)]
类型forall g. Gen g => g x
应该由具体数据类型(构造函数)表示。如果您有这样的值,比如a
,仅有效操作就是应用get
,给我们:
a :: forall g. Gen g => g x
get a :: forall g. [(x, g x)]
因此,我们发现g x
中包含的信息与[(x, g x)]
中包含的信息相同;如果我们想要一个可以代表任何此类g
的类型,我们采取最大的定点:
newtype G x = G { unG :: [(x, G x)] }
G
现在应该可以代表任何Gen
toG :: Gen g => g x -> G x
toG = G . map (second toG) . get
它本身就是Gen
instance Gen G where
get (G as) = as
这就像你想要的那样吗?