2016-10-08 98 views
3

我在使用幻象類型的困惑:幻影類型混淆?

type Words = String 
type Numbers = Int 

data NonPhantom = NP1 Words | NP2 Numbers deriving (Show) 

data Phantom a = P1 Words | P2 Numbers deriving (Show) 

nonPhantomFunction :: NonPhantom -> Int 
nonPhantomFunction r = 100 


phantomFunction :: Phantom Numbers -> Int 
phantomFunction a = 2001 


main = do 
    print $ nonPhantomFunction (NP1 "sdsdds") --can also pass NP2 here! 
    print $ phantomFunction (P1 "sdsdsd") --This shouldn't work!? 

我希望這個代碼編譯,因爲phantomFunction明確規定其預期的數據類型的NumbersPhantom

但是這個編譯好嗎?我究竟做錯了什麼?

+2

也許你想要一個GADT呢? – augustss

回答

4
data Phantom a = P1 Words | P2 Numbers deriving (Show) 

這使得任何類型的形式Phantom aP1 "aa",對於任何a,包括Numbers

1

構造函數的參數與構造函數所屬類型的類型參數之間沒有隱式連接。如果你想讓type參數表示的類型出現在構造函數的參數中的任何地方,你需要明確地聲明它。

您可以在下面的表達式也看到這一點:

Nothing 
[] 

第一個可以創建任何a任何a和第二列表[a]一個Maybe a

同樣

P1 "xyz" 

從你的例子可以使任何a

0

一個Phantom a其他的答案已經解釋

P1 :: Words -> Phantom a 

意味着它的構造函數的類型能夠構建P1 a類型的值爲任意的選擇a;特別是對於a ~ Numbers的選擇。這就是爲什麼你的函數調用

phantomFunction (P1 "sdsdsd") 

typechecks。

現在,你如何解決這個問題?我假設你想要P1 :: Words -> Phantom Words? GADTs允許您通過允許您寫

{-# LANGUAGE GADTs #-} 
data Phantom a where 
    P1 :: Words -> Phantom Words 
    P2 :: Numbers -> Phantom Numbers 

這將

  • 確保P1 _的類型爲Phantom Words,所以你不能建立一個Phantom Numbers約束在構建的價值的類型,發生的類型變量與它
  • 通過僅匹配上P1
  • 的Al允許消耗一個Phantom Words做詳盡模式匹配函數低於Phantom a多態的函數,以基於模式匹配來優化它們的類型(這是最大的),所以你可以編寫例如

    dup :: Phantom a -> a 
    dup (P1 ws) = ws ++ ws -- Here, we have to return a Words, and ws is a Words, so ++ will work 
    dup (P2 n) = n + n -- Here, we have to return a Numbers, and x is a Numbers, so + will work