在过去的几个月里,我一直在努力学习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]
Run Code Online (Sandbox Code Playgroud)
最后剩下的错误发生在线上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)
Run Code Online (Sandbox Code Playgroud)
看看有问题的功能......
call_f0 :: Gen gb => (Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, PROXY gb Char)]
call_f0 f0 g0 = f0 (proxyNotL g0)
Run Code Online (Sandbox Code Playgroud)
该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需要...嗯...是的,因为PROXY gb情况下Gen,我知道PROXY和gb,是的,我可以".
所以......为什么GHC拒绝统一ga和Proxy gb?为什么GHC拒绝调用多态函数,其参数值应该由该函数的多态类型支持?
或者,我在这里完全误解了什么?
您只需要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)]
Run Code Online (Sandbox Code Playgroud)
否则,GHC会放一个隐含forall ga.的最外层,即主叫方会得到来决定哪些ga应该被使用,而GHC已经正确地推断,这些功能必须能够选择自己认为ga应该是PROXY gb.
换句话说,当使用RankNTypes需要多态参数时,必须始终使用显式量词来指定它.
| 归档时间: |
|
| 查看次数: |
841 次 |
| 最近记录: |