簡體   English   中英

如何投射`forall a。 a -> a` 回到 `a -> a`?

[英]How to cast `forall a. a -> a` back to `a -> a`?

在我的實際問題中,我有一個函數f ,作為參數傳遞,它改變了列表中的順序,但對類型沒有要求,也不改變類型。 我想在[Int][Bool]上應用該函數,所以我必須解決試圖將f類型轉換為[Int] -> [Int][Bool] -> [Bool]兩個上下文。 我用Rank2Types解決了這個Rank2Types 但是隨后我在f等函數列表中使用any ,並且any要求函數為[a] -> [a]而不是forall a. [a] -> [a] forall a. [a] -> [a]

下面的代碼雖然荒謬,但完美地重現了錯誤:

{-# LANGUAGE Rank2Types #-}

--debug :: (forall a. [a] -> [a]) -> Bool 
debug swap = any combine [swap]
  where
    combine :: (forall a. [a] -> [a]) -> Bool
    combine f =  usefonBool f && usefonInt f
    --usefonBool :: (forall a. [a] -> [a]) -> Bool
    usefonBool f = f [True,True] == [False]

    --usefonInt :: (forall a. [a] -> [a]) -> Bool
    usefonInt f = (f [1,2]) == [2,1]

錯誤信息是:

• Couldn't match type ‘a’ with ‘forall a1. [a1] -> [a1]’
  ‘a’ is a rigid type variable bound by
    the inferred type of debug :: a -> Bool
    at /path/debug.hs:(4,1)-(12,36)
  Expected type: a -> Bool
    Actual type: (forall a. [a] -> [a]) -> Bool
• In the first argument of ‘any’, namely ‘combine’
  In the expression: any combine [swap]
  In an equation for ‘debug’:
      debug swap
        = any combine [swap]
        where
            combine :: (forall a. [a] -> [a]) -> Bool
            combine f = usefonBool f && usefonInt f
            usefonBool f = f [True, ....] == [False]
            usefonInt f = (f [1, ....]) == [2, ....]
• Relevant bindings include
    swap :: a (bound at /path/debug.hs:4:7)
    debug :: a -> Bool (bound at /path/debug.hs:4:1)
|

我的目標是找到一個注釋,讓我可以在不同類型上使用f ,然后將 any 應用於此類通用函數的列表。

如果我取消注釋所有類型注釋(或只是頂部注釋),錯誤將更改為

• Couldn't match type ‘[a0] -> [a0]’ with ‘forall a. [a] -> [a]’
  Expected type: ([a0] -> [a0]) -> Bool
    Actual type: (forall a. [a] -> [a]) -> Bool
• In the first argument of ‘any’, namely ‘combine’
  In the expression: any combine [swap]
  In an equation for ‘debug’:
      debug swap
        = any combine [swap]
        where
            combine :: (forall a. [a] -> [a]) -> Bool
            combine f = usefonBool f && usefonInt f
            usefonBool :: (forall a. [a] -> [a]) -> Bool
            usefonBool f = f [True, ....] == [False]
            ....
  |

首先,請注意Rank2Types是一個已棄用的名稱。 它相當於現代 GHC 中的RankNTypes ,這是擴展的首選名稱。

這是根本的問題。 “此類通用函數的列表”可以具有以下類型:

[forall a. [a] -> [a]]

遺憾的是,這不是有效的 Haskell 類型,因為 Haskell 不支持“不可預測的多態性”。 具體來說,以下程序:

{-# LANGUAGE RankNTypes #-}
myFunctions :: [forall a. [a] -> [a]]
myFunctions = [f1, f2]
   where f1 (x:y:rest) = y:x:rest
         f2 = reverse

產生錯誤信息:

 DebugRank.hs:2:16: error: • Illegal polymorphic type: forall a. [a] -> [a] GHC doesn't yet support impredicative polymorphism • In the type signature: myFunctions :: [forall a. [a] -> [a]]

有一個擴展, ImpredicativeTypes 它不完整且不完整,但它允許編譯以下內容:

{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ImpredicativeTypes #-}

myFunctions :: [forall a. [a] -> [a]]
myFunctions = [f1, f2]
   where f1 (x:y:rest) = y:x:rest
         f2 = reverse

debug :: [forall a. [a] -> [a]] -> Bool
debug = any combine
  where
    combine :: (forall a. [a] -> [a]) -> Bool
    combine f = usefonBool f && usefonInt f
    usefonBool f = f [True,True] == [False]
    usefonInt  f = f [1,2] == [2,1]

main = print (debug myFunctions)

不過,我仍然建議不要使用它。

通常的替代方法是對多態函數使用newtype包裝器:

newtype ListFunction = ListFunction (forall a. [a] -> [a])

這需要一些樣板,但除了RankNTypes之外沒有擴展:

myFunctions :: [ListFunction]
myFunctions = [ListFunction f1, ListFunction f2]
   where f1 (x:y:rest) = y:x:rest
         f2 = reverse

debug :: [ListFunction] -> Bool
debug = any combine
  where
    combine :: ListFunction -> Bool
    combine (ListFunction f) = usefonBool f && usefonInt f
    usefonBool f = f [True,True] == [False]
    usefonInt  f = f [1,2] == [2,1]

完整代碼:

{-# LANGUAGE RankNTypes #-}

newtype ListFunction = ListFunction (forall a. [a] -> [a])

myFunctions :: [ListFunction]
myFunctions = [ListFunction f1, ListFunction f2]
   where f1 (x:y:rest) = y:x:rest
         f2 = reverse

debug :: [ListFunction] -> Bool
debug = any combine
  where
    combine :: ListFunction -> Bool
    combine (ListFunction f) = usefonBool f && usefonInt f
    usefonBool f = f [True,True] == [False]
    usefonInt  f = f [1,2] == [2,1]

main = print $ debug myFunctions

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM