簡體   English   中英

為什么 Haskell 不接受我的組合“zip”定義?

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

這是教科書的zip功能:

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

我早些時候在#haskell 上問過“zip”是否可以單獨使用“foldr”來實現,沒有遞歸,沒有模式匹配。 經過一些思考,我們注意到可以使用延續來消除遞歸:

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

我們還剩下模式匹配。 經過更多的神經元敬酒,我想出了一個我認為合乎邏輯的不完整答案:

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

它返回一個平面列表,但會進行壓縮。 我確信這是有道理的,但 Haskell 抱怨這種類型。 我繼續在一個無類型的 lambda 計算器上測試它,它起作用了。 為什么 Haskell 不能接受我的函數?

錯誤是:

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

至於為什么你的定義不被接受:看看這個:

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

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

所以如果你想使用第一個函數作為foldr的參數,你會得到(如果你匹配foldr第一個參數的類型:

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

這當然是一個問題(因為r(r -> [a]) -> [a]相互遞歸並且都應該等於b'

這就是編譯器告訴你的

如何修復它

您可以使用修復您的想法

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

它的原始用途中借來的

有了這個,你可以寫:

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

你會得到:

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

這是(我認為)你想要的。

很明顯,您的兩個列表都需要屬於同一類型,所以我不知道這是否真的對您有幫助

我們可以通過定義一個函數來消除顯式模式匹配。

是作弊嗎? 如果maybebool被允許,則不會,因為它們是; 那么我們也應該允許list (也在extraData.List.Extra ),

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

一樣; 這樣我們就可以在您的zip'定義中

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

或例如

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

如果你喜歡這種東西。

關於你的錯誤,“infinite type”往往表示自己應用; 事實上,無論你的zipper返回什么,你都是在自我應用它,在你的

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

我試圖調整你的定義並想出了

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
-}

它似乎在紙上正確減少,但我仍然在這里遇到了無限的類型錯誤。

現在沒有(立即明顯的)自我應用程序,但是第一個 zip 獲得的延續類型取決於第一個 zip 本身的類型; 所以仍然存在循環依賴: tqtq ~ a -> tr -> [(a,b)] ~ a -> (tq -> [(a,b)]) -> [(a,b)]

事實上,這是我得到的兩種類型錯誤,(第一個是關於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

在使用foldr和 continuations 的通常定義中,這些 continuation 的類型是獨立的; 這就是它在那里工作的原因,我猜。

我可以為您提供一個稍微不同的觀點(我認為),以得出與 Carsten 類似的解決方案(但類型更簡單)。

這是您的代碼,用於您的“編織 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]    

所以,這是無限類型。 Haskell 不允許將其用於任意類型(這就是類型變量所代表的含義)。

但是 Haskell 的數據類型確實承認遞歸。 列表、樹等——所有常見的類型都是遞歸的。 允許的:

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

在這里,我們對等式兩邊相同類型的,就如我們tr的類型等價,兩側tr ~ tr -> [a] 但它是一種特定類型,而不是任意類型。

所以我們只是聲明它,遵循上面的“方程”:

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

什么是Tree a類型? 它是進入Branch的“東西”,它是一Tree a 給定的樹不必無限構造,因為undefined也具有Tree a類型。

什么是TR a類型? 它是進入TR a -> [a]的“東西”,這是一個TR a 給定的TR a不必無限構造,因為const []也可以是TR a類型。

我們想要的遞歸類型tr ~ tr -> [a]已經成為真正的遞歸類型定義newtype TR a = Pack { TR a -> [a] } ,隱藏在數據構造函數Pack后面(它將被刪除編譯器,由於使用了newtype關鍵字,但這是一個無關緊要的細節;它也適用於data )。

Haskell 在這里為我們處理遞歸。 類型理論家喜歡自己處理這個問題,用Fix 但是 Haskell 用戶已經可以使用該語言。 我們不必了解它是如何實現的,就可以使用它。 無需重新發明輪子,直到我們想自己構建它。

所以, zipper xs類型是tr 現在它變成了TR a ,所以這就是新的zipper xs必須返回的東西——“打包”的列表生成函數。 foldr組合函數必須返回zipper調用返回的內容(根據foldr定義的優點)。 要應用打包函數,我們現在需要先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