如何在Haskell中使用代理? (可能使用更高等级的类型扩展) [英] How to use a proxy in Haskell? (probably using a higher-rank types extension)

查看:102
本文介绍了如何在Haskell中使用代理? (可能使用更高等级的类型扩展)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去的几个月里,我一直在认真学习Haskell - 之前,我是一个看似永恒的新手,对基础知识的知识非常有限。在试图将我的新知识付诸实践的过程中,我始终发现我希望使用基于类型类的类似代理的模式。前几次,当我弄清楚为什么它不起作用时,我把它解释为好吧 - 我可能找不到一个单一的惯用Haskell替代品,但是这里的问题是,我使用了错误的方法该语言。但是我发现我真的很 不喜欢不能做类似代理的事情。



尝试获取更深入的理解为什么我不能使用代理服务器,经过大量的实验后,我终于发现,使用GHC更高等级类型的扩展,也许我可以有代理服务器。但是我仍然无法完成它的工作,我不知道为什么。



这是我管理的最好的代码...

p>

  { - #LANGUAGE RankNTypes# - } 
模块测试其中

- 简单类型类基于解析器组合器。
class Gen g where
get :: g x - > [(x,gx)]

实例Gen []其中
get [] = []
get(x:xs)= [(x,xs)]

- 代理类型 - 保存一对包含...
- - 某种支持Gen $ b $的类型的值 - - 一个函数用于指示何时应该跳过一个项目
newtype PROXY nestedgen x =代理(nestedgen x,x - > Bool)

proxyskip :: Gen nestedgen => PROXY nestedsted r - > Bool
proxyskip(Proxy(g,predf))= case得到g
[] - >假
((r,_):_) - > predf r

proxyget :: Gen nestedgen => PROXY nestedsted r - > [(r,PROXY nestedgen r)]
proxyget pr @(Proxy(sg,predf))= if proxyskip pr
then [(r2,g2)| (代理(g1,predf))]
else [(r3,Proxy(g3,predf))| (r3,g3)< - get sg]

- PROXY的Gen的实例 - 在适当的地方获得跳过的项目
实例Gen nestedgen => Gen(PROXY nestedgen)其中
get = proxyget

- 测试解析器
- 获取指定数量的项目,将它们作为列表提供(在$ b $内b - 非确定性(结果,状态)对的列表)。
getN :: Gen g => Int - > g x - > [([x],g x)]
getN n g | (n <0)=错误负n
| (n == 0)= [([],g)]
| True = [(r1:r2,g2)| (r1,g1)< - get g,(r2,g2)< - getN(n-1)g1]

- 在PROXY中包装一些任意的parser字母'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) ...

 <$ c编译测试(Test.hs,Test.o)

Test.hs:44:21:
无法推断(ga〜PROXY gb)$ b来自上下文(Gen gb)的
$ b由
的类型签名绑定call_f0 :: Gen gb =>
(Gen ga => ga Char - > [(r,ga Char)])
- > gb Char
- > [(r,PROXY gb Char)]
at Test.hs:44:1-33
`ga'是一个刚性类型变量,由
绑定
的类型签名call_f0 :: Gen gb =>
(Gen ga => ga Char - > [(r,ga Char)])
- > gb Char
- > [(r,PROXY gb Char)]
at Test.hs:44:1
预期类型:ga Char
实际类型:PROXY gb Char
在返回类型a调用`proxyNotL'
在`f0'的第一个参数中,即`(proxyNotL g0)'
在表达式中: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 的东西,并且有一个实例声明实例Gen nestedgen => Gen(PROXY nestedgen)其中... ,这样 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 => (其中ga = Gen(>> ga Char  - > [(r,ga Char)]) - > gb Char  - > [(r,PROXY gb Char)] 
test :: Gen gb => (其中ga = Gen(>> ga Char - > [(r,ga Char)]) - > gb Char - > [(r,gb Char)]

否则,GHC会将一个隐含的在最外层,即调用者可以决定应该使用哪一个 ga ,而GHC已经正确地推断出这些函数必须能够自行选择 ga 应该是 PROXY gb


$ b $换句话说,当使用 RankNTypes 来要求多态参数时,您必须始终使用明确的量词来指定它。


For the last few months, I've been putting some serious effort into learning Haskell - previously, I was a seemingly perpetual newbie with a very limited knowledge of the basics. While trying to put my new knowledge into practice on few-steps-up-from-hello-world projects, I keep finding that I want to use proxy-like patterns based on type classes. The first couple of times, when I figured out why it wasn't working, I dismissed it as "OK - I may not find a single idiomatic Haskell replacement, but odds are the problem here is that I'm using the wrong approach for the language". But what I've found is that I really really don't like not being able to do proxy-like things.

Trying to get a deeper understanding of why I couldn't use a proxy, after lots of experimenting, I finally figured out that with GHCs higher rank types extension, perhaps I can have proxies. But I still can't quite make it work, and I'm not sure why.

Here is the code for the best I've managed...

{-# 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]

The last remaining error occurs on the line 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)

Looking at the problematic function...

call_f0 :: Gen gb => (Gen ga => ga Char -> [(r, ga Char)]) -> gb Char -> [(r, PROXY gb Char)]
call_f0 f0 g0 = f0 (proxyNotL g0)

The f0 function is (if I understand higher rank types correctly) a polymorphic function passed as a parameter, with type Gen ga => ga Char -> [(r, ga Char)]. In translating-to-C terms, the caller has passed in a function pointer, but has not supplied the vtable pointer.

The proxyNotL function returns something of type PROXY gb Char, and there's an instance declaration instance Gen nestedgen => Gen (PROXY nestedgen) where ..., so that PROXY gb Char instances Gen provided gb instances Gen, which it does according to the type signature for call_f0.

Basically, as far as I can tell, GHC should say "can I provide the vtable that f0 requires... hmmm... yes, since PROXY gb instances Gen, and I know about PROXY and gb, yes I can".

So... why is GHC refusing to unify ga and Proxy gb? Why is GHC refusing to call a polymorphic function, with an argument value that should be supported by the polymorphic type of that function?

Or alternatively, what am I completely misunderstanding here?

解决方案

You just need to explicitly specify that the function f0 must be polymorphic by adding the universal quantifier forall ga. to your type signatures:

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)]

Otherwise, GHC will put an implicit forall ga. at the outermost level, meaning that the caller would get to decide which ga should be used, while GHC has correctly deduced that these functions must be able to choose themselves that ga should be PROXY gb.

In other words, when using RankNTypes to require a polymorphic argument, you must always use an explicit quantifier to specify this.

这篇关于如何在Haskell中使用代理? (可能使用更高等级的类型扩展)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆