2014-11-24 56 views
2

在下面的示例中,我試圖使foo返回其「預期」多態輸出類型。想法是foo返回一個多態值和一個存在類型,然後bar指定元組的類型爲隱藏類型。 (當然,這只是工作,如果在bar種類也存在,這是真的在我的情況)以下示例編譯:帶約束條件的循環打字

{-# LANGUAGE GADTs, ScopedTypeVariables #-} 

module Foo where 

import Data.Proxy 
import Data.Typeable 

data HiddenType where 
    Hidden :: (Typeable a) => Proxy a -> HiddenType 

foo :: (i,HiddenType) 
foo = (undefined, Hidden (Proxy::Proxy Int)) 

data Foo where 
    Foo :: i -> Foo 

bar :: Foo 
bar = 
    let (x,h) = foo 
    in case h of 
    (Hidden (p::Proxy i)) -> Foo (x :: i) 

我真的需要foo一個Typeable約束:

foo :: (Typeable i) => (i,HiddenType) 

當我再補充一點約束(沒有其他變化),我得到這些錯誤:

Foo.hs:20:15: 
    No instance for (Typeable t0) arising from a use of ‘foo’ 
    The type variable ‘t0’ is ambiguous 
    Relevant bindings include x :: t0 (bound at Foo.hs:20:8) 
    Note: there are several potential instances: 
     instance [overlap ok] Typeable() 
     -- Defined in ‘Data.Typeable.Internal’ 
     instance [overlap ok] Typeable Bool 
     -- Defined in ‘Data.Typeable.Internal’ 
     instance [overlap ok] Typeable Char 
     -- Defined in ‘Data.Typeable.Internal’ 
     ...plus 14 others 
    In the expression: foo 
    In a pattern binding: (x, h) = foo 
    In the expression: 
     let (x, h) = foo 
     in case h of { (Hidden (p :: Proxy i)) -> Foo (x :: i) } 

Foo.hs:22:35: 
    Couldn't match expected type ‘a’ with actual type ‘t0’ 
     because type variable ‘a’ would escape its scope 
    This (rigid, skolem) type variable is bound by 
     a pattern with constructor 
     Hidden :: forall a. Typeable a => Proxy a -> HiddenType, 
     in a case alternative 
     at Foo.hs:22:6-24 
    Relevant bindings include 
     p :: Proxy a (bound at Foo.hs:22:14) 
     x :: t0 (bound at Foo.hs:20:8) 
    In the first argument of ‘Foo’, namely ‘(x :: i)’ 
    In the expression: Foo (x :: i) 
Failed, modules loaded: none. 

我明白,約束變成共同的論點所以在我看來,這裏的問題是GHC無法處理GADT的模式綁定。如果可以,我可以用一個遞歸讓利給這樣說:

bar :: Foo 
bar = 
    let (x :: i,h) = foo 
     (Hidden (p::Proxy i)) = h 
    in Foo x 

這應該使範圍的限制,提供額外的參數foo。我在這裏的目的是使h包含一些(隱藏)具體類型i,它應作爲具體類型的多態函數 GHC抱怨:

Foo.hs:19:8: 
    You cannot bind scoped type variable ‘i’ 
     in a pattern binding signature 
    In the pattern: x :: i 
    In the pattern: (x :: i, h) 
    In a pattern binding: 
     (x :: i, h) = foo 

Foo.hs:20:8: 
    My brain just exploded 
    I can't handle pattern bindings for existential or GADT data constructors. 
    Instead, use a case-expression, or do-notation, to unpack the constructor. 
    In the pattern: Hidden (p :: Proxy i) 
    In a pattern binding: (Hidden (p :: Proxy i)) = h 
    In the expression: 
     let 
     (x :: i, h) = foo 
     (Hidden (p :: Proxy i)) = h 
     in Foo x 

我使用情況下的假設是 1. foo 2.同時計算iHiddenType 2.隱藏類型的值涉及(至少部分)第一個元組元素的計算。這意味着我做而不是想在bar中撥打foo兩次(一次獲得HiddenType,並且一次使用該類型綁定第一個元組元素)。 在foo上存在約束時,是否有某種方法可以使bar的定義成爲可能?

+2

你究竟想要做什麼?這很少有意義。特別是最後一種情況,你試圖用''Hidden'內部的其他類型來統一類型變量 - 恰恰與你可以用存在類型做什麼相反。第一種情況不起作用,因爲您需要選擇一個實例,以便可以解析'Typeable i'約束,例如'let(x,h)= foo'必須是'let(x,h)= foo: :(Int,HiddenType)',但這不起作用,因爲你不能從'Hidden'內部神奇地產生一個進入。 – user2407038 2014-11-24 05:54:05

+2

你可能試圖實現'Data.Dynamic'嗎? – user2407038 2014-11-24 05:54:23

+0

@ user2407038聽起來很像'Data.Dynamic'。這個例子*真的被切斷了,但是它的核心問題是關於AST中的變量類型。 'foo'就像一個'lambda',其中隱藏類型是變量的(預期)類型。 'bar'就像'let',它需要lambda變量的類型,但是隱藏了那個類型。 – crockeea 2014-11-24 14:20:49

回答

3

我想問題是foo的返回值實際上並不是多態。 foo本身是多態的,但返回值必須存在於特定類型中。不幸的是,您要使用的類型尚不可用,並且由於有循環參考,因此無法在foo的呼叫站點處列入範圍。如果我們寫出來的僞核心的foo定義,這個問題是清楚的:

foo (@ iType) _ = (undefined @ iType, HiddenType...) 

這裏@ iType是一種說法。在獲取HiddenType之前,我們需要首先完成foo的類型應用程序(和字典應用程序,這是未使用的),因此無法按原樣執行此操作。

幸運的是,有辦法說服GHC是foo應該返回一個實際的多態值:

{-# LANGUAGE GADTs, ScopedTypeVariables #-} 
{-# LANGUAGE ImpredicativeTypes #-} 

module Foo where 

import Data.Proxy 
import Data.Typeable 

data HiddenType where 
    Hidden :: (Typeable a) => Proxy a -> HiddenType 

foo :: (forall i. Typeable i => i,HiddenType) 
foo = (undefined, Hidden (Proxy::Proxy Int)) 

data Foo where 
    Foo :: i -> Foo 

bar = 
    let (x,h) = foo 
    in case h of 
    Hidden p -> Foo (x `asProxyTypeOf` p) 

如果你熟悉較高等級的類型(例如RankNTypes擴展名),你能想到的ImpredicativeTypes作爲類似的東西,except for data structures instead of functions。例如,如果沒有ImpredicativeTypes你可以寫:

list1 :: forall t. Typeable t => [t] 

它是一個包含t類型的所有值,對於一些tTypeable約束列表的類型。即使它是多態的,列表中的每個元素都將是相同的類型!相反,如果你要移動的forall名單內,讓每一個元件可以是不同類型的tImpredicativeTypes將允許這樣的:

list2 :: [forall t. Typeable t => t] 

這不是一個支持常用擴展,但它是偶爾有用。

foo的impredicative版本的核心是有點不同,以及:

foo = (\(@ iType) _ -> undefined @ iType, HiddenType...) 

你可以看到,這允許您添加註釋到letx是爲所需多:

bar :: Foo 
bar = 
    let (x :: forall i. Typeable i => i,h) = foo 
    in case h of 
    Hidden p -> Foo (x `asProxyTypeOf` p) 

這允許您在隱藏類型中延遲x的實例化,直到它可用時爲止。儘管如此,您仍然需要在Foo或另一個Hidden範圍內進行裝箱,因爲ghc不允許類型在第一個Hidden模式匹配下退出。

+1

'forall i。可打字的i => i'只有'undefined'和'list2 :: [forall t。只能用[]鍵入t => t]',所以我看不出這是有用的。你當然不應該將ImpredictiveTypes與RankNTypes類似 - 後者不能以任何方式破壞工作程序,而前者幾乎肯定會這樣做。 ImpredictiveTypes是邪惡的,應該避免。 – user2407038 2014-11-24 05:59:06

+0

@ user2407038:當然,這對於'Typeable'並沒有什麼用處,但我想這是一個嚴格削減的OP例子。除非必要,否則我不會啓用「ImpredicativeTypes」,但它似乎正是OP在此處詢問的內容。另外,您能否提供一個打破工作計劃的擴展示例? GHC本身不會推斷它,所以只要啓用「ImpredicativeTypes」似乎不會導致許多問題,除非是人爲的例子。 – 2014-11-24 06:20:51

+0

此外,impredicative多態只是一個更高級別類型的概括。想想隱含類型的最好方法是明確地操作量詞(並且這是在Haskell中使用它們的唯一方法),此時這種關係是顯而易見的。 – 2014-11-24 10:05:24