简体   繁体   English

为什么 Haskell 不接受我的组合“zip”定义?

[英]Why Haskell doesn't accept my combinatoric “zip” definition?

This is the textbook zip function:这是教科书的zip功能:

zip :: [a] -> [a] -> [(a,a)]
zip [] _ = []
zip _ [] = []
zip (x:xs) (y:ys) = (x,y) : zip xs ys

I asked on #haskell earlier wether "zip" could be implemented using "foldr" alone, no recursion, no pattern matching.我早些时候在#haskell 上问过“zip”是否可以单独使用“foldr”来实现,没有递归,没有模式匹配。 After some thinking, we noticed the recursion could be eliminated using continuations:经过一些思考,我们注意到可以使用延续来消除递归:

zip' :: [a] -> [a] -> [(a,a)]
zip' = foldr cons nil
    where
        cons h t (y:ys) = (h,y) : (t ys)
        cons h t []     = []
        nil             = const []

We are still left with pattern matching.我们还剩下模式匹配。 After some more neuron toasting I came up with an incomplete answer that I thought was logical:经过更多的神经元敬酒,我想出了一个我认为合乎逻辑的不完整答案:

zip :: [a] -> [a] -> [a]
zip a b = (zipper a) (zipper b) where
    zipper = foldr (\ x xs cont -> x : cont xs) (const [])

It returns a flat list, but does the zipping.它返回一个平面列表,但会进行压缩。 I was certain it made sense, but Haskell complained about the type.我确信这是有道理的,但 Haskell 抱怨这种类型。 I proceeded to test it on a untyped lambda calculator, and it worked.我继续在一个无类型的 lambda 计算器上测试它,它起作用了。 Why can't Haskell accept my function?为什么 Haskell 不能接受我的函数?

The error is:错误是:

zip.hs:17:19:
    Occurs check: cannot construct the infinite type:
      t0 ~ (t0 -> [a]) -> [a]
    Expected type: a -> ((t0 -> [a]) -> [a]) -> (t0 -> [a]) -> [a]
      Actual type: a
                   -> ((t0 -> [a]) -> [a]) -> (((t0 -> [a]) -> [a]) -> [a]) -> [a]
    Relevant bindings include
      b ∷ [a] (bound at zip.hs:17:7)
      a ∷ [a] (bound at zip.hs:17:5)
      zip ∷ [a] -> [a] -> [a] (bound at zip.hs:17:1)
    In the first argument of ‘foldr’, namely ‘cons’
    In the expression: ((foldr cons nil a) (foldr cons nil b))

zip.hs:17:38:
    Occurs check: cannot construct the infinite type:
      t0 ~ (t0 -> [a]) -> [a]
    Expected type: a -> (t0 -> [a]) -> t0 -> [a]
      Actual type: a -> (t0 -> [a]) -> ((t0 -> [a]) -> [a]) -> [a]
    Relevant bindings include
      b ∷ [a] (bound at zip.hs:17:7)
      a ∷ [a] (bound at zip.hs:17:5)
      zip ∷ [a] -> [a] -> [a] (bound at zip.hs:17:1)
    In the first argument of ‘foldr’, namely ‘cons’
    In the fourth argument of ‘foldr’, namely ‘(foldr cons nil b)’

As to why your definition is not accepted: look at this:至于为什么你的定义不被接受:看看这个:

λ> :t \ x xs cont -> x : cont xs
 ... :: a -> r -> ((r -> [a]) -> [a])

λ> :t foldr
foldr :: (a' -> b' -> b') -> b' -> [a'] -> b'

so if you want to use the first function as an argument for foldr you get (if you match the types of foldr s first argument :所以如果你想使用第一个函数作为foldr的参数,你会得到(如果你匹配foldr第一个参数的类型:

a' := a
b' := r
b' := (r -> [a]) -> [a]

which of course is a problem (as r and (r -> [a]) -> [a] mutual-recursive and should both be equal to b' )这当然是一个问题(因为r(r -> [a]) -> [a]相互递归并且都应该等于b'

That is what the compiler tells you这就是编译器告诉你的

how to repair it如何修复它

You can repair your idea using您可以使用修复您的想法

newtype Fix a t = Fix { unFix :: Fix a t -> [a] }

which I borrowed form it original use .它的原始用途中借来的

With this you can write:有了这个,你可以写:

zipCat :: [a] -> [a] -> [a]
zipCat a b = (unFix $ zipper a) (zipper b) where
  zipper = foldr foldF (Fix $ const [])
  foldF x xs = Fix (\ cont -> x : (unFix cont $ xs))

and you get:你会得到:

λ> zipCat [1..4] [5..8]
[1,5,2,6,3,7,4,8]

which is (what I think) you wanted.这是(我认为)你想要的。

BUT obvious here both of your lists needs to be of the same type so I don't know if this will really help you很明显,您的两个列表都需要属于同一类型,所以我不知道这是否真的对您有帮助

We can eliminate the explicit pattern matching by defining a function that will do it for us.我们可以通过定义一个函数来消除显式模式匹配。

Is it cheating?是作弊吗? Not if maybe and bool are allowed, as they are;如果maybebool被允许,则不会,因为它们是; then we should also allow list (also in extra 's Data.List.Extra ),那么我们也应该允许list (也在extraData.List.Extra ),

list :: b -> (a -> [a] -> b) -> [a] -> b 
list n c []     = n
list n c (x:xs) = c x xs

just the same;一样; so that we can have, in your zip' definition,这样我们就可以在您的zip'定义中

cons h t = list [] (\y ys -> (h,y) : t ys)

or eg或例如

         = list [] (uncurry ((:).(h,).fst <*> t.snd))
         = list [] (curry $ uncurry (:) . ((h,) *** t))
         = list [] (flip ((.) . (:) . (h,)) t)
         = list [] ((. t) . (:) . (h,))

if you prefer this kind of thing.如果你喜欢这种东西。

About your error, "infinite type" often indicates self application;关于你的错误,“infinite type”往往表示自己应用; indeed, whatever your zipper returns, you're self-applying it, in your事实上,无论你的zipper返回什么,你都是在自我应用它,在你的

zip a b = (zipper a) (zipper b)  where ....

I tried to tweak your definition and came up with我试图调整你的定义并想出了

zipp :: [a] -> [b] -> [(a,b)]
zipp xs ys = zip1 xs (zip2 ys)
  where
     -- zip1 :: [a] -> tq -> [(a,b)]          -- zip1 xs :: tr ~ tq -> [(a,b)]
     zip1 xs q = foldr (\ x r q -> q x r ) n xs q 
                       -------- c --------
     n    q  = []

     -- zip2 :: [b] -> a -> tr -> [(a,b)]     -- zip2 ys :: tq ~ a -> tr -> [(a,b)]
     zip2 ys x r = foldr (\ y q x r -> (x,y) : r q ) m ys x r  
                         ---------- k --------------
     m  x r  = []

{-
  zipp [x1,x2,x3] [y1,y2,y3,y4]

= c x1 (c x2 (c xn n)) (k y1 (k y2 (k y3 (k y4 m))))
       ---------------       ----------------------
        r                     q

= k y1 (k y2 (k y3 (k y4 m))) x1 (c x2 (c xn n))
       ----------------------    ---------------
        q                         r
-}

It seems to reduce correctly on paper, but still I got the infinite type errors here too.它似乎在纸上正确减少,但我仍然在这里遇到了无限的类型错误。

There is no (immediately apparent) self-application now, but the type of the continuation that the first zip gets, depends on the type of that first zip itself;现在没有(立即明显的)自我应用程序,但是第一个 zip 获得的延续类型取决于第一个 zip 本身的类型; so still there's a circular dependency: tq is on both sides of the type equivalency in tq ~ a -> tr -> [(a,b)] ~ a -> (tq -> [(a,b)]) -> [(a,b)] .所以仍然存在循环依赖: tqtq ~ a -> tr -> [(a,b)] ~ a -> (tq -> [(a,b)]) -> [(a,b)]

Indeed that's the two type errors that I get, (the first one is about the tr type),事实上,这是我得到的两种类型错误,(第一个是关于tr类型的),

Occurs check: cannot construct the infinite type:
  t1 ~ (a -> t1 -> [(a, b)]) -> [(a, b)]          -- tr

Occurs check: cannot construct the infinite type:
  t0 ~ a -> (t0 -> [(a, b)]) -> [(a, b)]          -- tq

In the usual definitions using foldr with continuations, the type of those continuations is independent;在使用foldr和 continuations 的通常定义中,这些 continuation 的类型是独立的; that's the reason that it works there, I guess.这就是它在那里工作的原因,我猜。

I can offer you a slightly different perspective (I think) to arrive at a similar solution as Carsten's (but with simpler types).我可以为您提供一个稍微不同的观点(我认为),以得出与 Carsten 类似的解决方案(但类型更简单)。

Here's your code again, for your "weaving zip" (I'm writing tr for "the type of r ", similarly tq for "the type of q "; I always use " r " for the recursive result argument of combining function in foldr definitions, as a mnemonic device):这是您的代码,用于您的“编织 zip”(我正在为“ r的类型”编写tr ,类似地为“ q的类型”编写tq ;我总是使用“ r ”作为组合函数的递归结果参数foldr定义,作为助记符):

zipw :: [a] -> [a] -> [a]
zipw xs ys = (zipper xs) (zipper ys) where
    zipper xs q = foldr (\ x r q -> x : q r) (const []) xs q
                        --- c -------------- --- n ----

 -- zipper [x1,x2,x3] (zipper ys) =
 -- c x1 (c x2 (c x3 n)) (zipper ys)
         --- r --------  --- q -----  tr ~ tq ; q r :: [a]
                                      --     => r r :: [a]
                                      -- => r :: tr -> [a] 
                                      --   tr ~  tr -> [a]    

So, this is the infinite type.所以,这是无限类型。 Haskell doesn't allow this for an arbitrary type (which is what type variables stand for). Haskell 不允许将其用于任意类型(这就是类型变量所代表的含义)。

But Haskell's datatypes do actually admit recursion.但是 Haskell 的数据类型确实承认递归。 Lists, trees, etc. — all the usual types are recursive.列表、树等——所有常见的类型都是递归的。 This is allowed:允许的:

data Tree a = Branch (Tree a) (Tree a)

Here we do have the same type on both sides of the equation, just as we have tr on both sides of the type equivalency, tr ~ tr -> [a] .在这里,我们对等式两边相同类型的,就如我们tr的类型等价,两侧tr ~ tr -> [a] But it's a specific type, not an arbitrary one.但它是一种特定类型,而不是任意类型。

So we just declare it so, following the above "equation":所以我们只是声明它,遵循上面的“方程”:

newtype TR a = Pack { unpack :: TR a -> [a] } 
           -- unpack :: TR a -> TR a -> [a]

What's a Tree a type?什么是Tree a类型? It's "something" that goes into a Branch , which is a Tree a .它是进入Branch的“东西”,它是一Tree a A given tree doesn't have to be infinitely constructed, because undefined has type Tree a too.给定的树不必无限构造,因为undefined也具有Tree a类型。

What's a TR a type?什么是TR a类型? It's "something" that goes into TR a -> [a] , which is a TR a .它是进入TR a -> [a]的“东西”,这是一个TR a A given TR a doesn't have to be infinitely constructed, because const [] can be of type TR a too.给定的TR a不必无限构造,因为const []也可以是TR a类型。

Our wannabe recursive type tr ~ tr -> [a] has become bona fide recursive type definition newtype TR a = Pack { TR a -> [a] } , hiding behind the data constructor, Pack (which will be gotten rid of by the compiler, thanks to the newtype keyword being used, but that's an extraneous detail; it works with data too).我们想要的递归类型tr ~ tr -> [a]已经成为真正的递归类型定义newtype TR a = Pack { TR a -> [a] } ,隐藏在数据构造函数Pack后面(它将被删除编译器,由于使用了newtype关键字,但这是一个无关紧要的细节;它也适用于data )。

Haskell handles the recursivity for us here. Haskell 在这里为我们处理递归。 Type theoreticians love to deal with this themselves, with Fix and whatnot;类型理论家喜欢自己处理这个问题,用Fix but a Haskell user already has this available to them, in the language.但是 Haskell 用户已经可以使用该语言。 We don't have to understand how it is implemented, to be able to use it.我们不必了解它是如何实现的,就可以使用它。 No need to reinvent the wheel until we want to build it ourselves.无需重新发明轮子,直到我们想自己构建它。

So, zipper xs had type tr ;所以, zipper xs类型是tr now it becomes TR a , so this is what the new zipper xs must return — the "packed" list-producing function.现在它变成了TR a ,所以这就是新的zipper xs必须返回的东西——“打包”的列表生成函数。 The foldr combining function must return what the zipper call returns (by the virtues of foldr definition). foldr组合函数必须返回zipper调用返回的内容(根据foldr定义的优点)。 To apply the packed function we now need to unpack it first:要应用打包函数,我们现在需要先unpack它:

zipw :: [a] -> [a] -> [a]
zipw xs ys = unpack (zipper xs) (zipper ys)
    where
    zipper :: [a] -> TR a
    zipper = foldr (\ x r -> Pack $ \q -> x : unpack q r)
                   (Pack $ const [])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM