如何在Haskell中使用代理?(可能使用更高级别的类型扩展)

Ste*_*314 5 proxy haskell ghc

在过去的几个月里,我一直在努力学习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,我知道PROXYgb,是的,我可以".

所以......为什么GHC拒绝统一gaProxy gb?为什么GHC拒绝调用多态函数,其参数值应该由该函数的多态类型支持?

或者,我在这里完全误解了什么?

ham*_*mar 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)]
Run Code Online (Sandbox Code Playgroud)

否则,GHC会放一个隐含forall ga.的最外层,即主叫方会得到来决定哪些ga应该被使用,而GHC已经正确地推断,这些功能必须能够选择自己认为ga应该是PROXY gb.

换句话说,当使用RankNTypes需要多态参数时,必须始终使用显式量词来指定它.