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

时间:2012-02-09 23:51:58

标签: haskell proxy 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]

最后一个错误发生在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,我知道PROXYgb,是的,我可以“。

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

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

2 个答案:

答案 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

这就像你想要的那样吗?