2017-07-28 117 views
1

我不清楚如何在haskell中編寫函數簽名,特別是使用Maybe。考慮:定義haskell函數,可能在簽名

f :: Maybe a -> Maybe a 
f = \a -> a 

main = print (f (Just 5)) 

這有效,但爲什麼不能這個函數簽名就是這個?

f :: Maybe -> Maybe 

因爲f只是需要一個Maybe類型,並返回一個Maybe類型。

相關:如果我想讓Maybe類型更具體,並且是Maybe Int,爲什麼這不起作用?

f :: Maybe Int a -> Maybe Int a 
f = \a -> a 

main = print (f (Just (Int 5))) 

(我正在使用runhaskell test.hs所有代碼)

+6

您似乎對類型簽名中「a」代表的含義感到困惑。 '也許'不是一個類型,它是*類型的構造函數*。你提供一個類型,你得到一個新的類型,比如'Maybe Int','Maybe Bool','Maybe [String]'或Maybe(Maybe Char)'。 '也許'本身不是一個有效的類型,所以'也許 - >也許'也不是一個有效的類型。 'a'是一個*類型的變量*,它代表了一些調用者提供的類型,所以'也許a - >也許a''意味着'a'可以是任何類型,只要它在兩邊都是相同的。 'a'與你的lambda中的'a'沒有關係,它完全位於另一個命名空間中。 –

+6

由於沒有人提到它,所以'Int 5'只有在'Int'是一個構造函數時(如'Just','Left')纔有效,但它不是。如果你想說'5'是'Int'類型,你可以使用類型註解('5 :: Int'),但在這種情況下它不是必需的。 – SwiftsNamesake

回答

10

好像你感到困惑類型變量。首先,在

f :: Maybe a -> Maybe a 
f = \a -> a 

a的第一行無關與a「在第二行S,我們可以這樣寫:

f :: Maybe a -> Maybe a 
f = \x -> x 

甚至

f :: Maybe foo -> Maybe foo 
f = \bar -> bar 

a是代表類型的變量。所以f這裏宣佈f有一次一大堆類型:

f :: Maybe Int -> Maybe Int 
f :: Maybe String -> Maybe String 
f :: Maybe (Maybe Bool) -> Maybe (Maybe Bool) 
... 

等。這不是我懷疑你認爲的一些「標籤」的論點。兩個a都相同的事實意味着參數類型必須是相同的結果類型。如果我們說f :: Maybe a -> Maybe b我們會得到這個家庭:

f :: Maybe Int -> Maybe Bool 
f :: Maybe String -> Maybe String 
f :: Maybe (Maybe Bool) -> Maybe Int 
... 

也就是ab現在可以代表不同的類型,但參數和結果還是要Maybe

的原因,你不能說

f :: Maybe -> Maybe 

是因爲Maybe不是一個類型 - 這是一個類型構造。如果你給它一個類型,它會給你一個類型。所以Maybe IntMaybe String是類型,一般Maybe a是一種類型,只要a是一種類型。

Maybe Int a(這是解析(Maybe Int) a)沒有意義,因爲Maybe Int不是一個類型的構造函數 - 它不接受任何更多的參數。

建議閱讀:Types and Typeclasses來自LYAH。

5

Maybe是一個類型構造函數,實質上是一個類型級函數。它需要一個類型(如Int)並返回一個類型(如Maybe Int)。類型的「類型」被稱爲種類:具有值的類型的種類,如Int,稱爲*。採用一個參數的類型構造函數的種類是* -> *。您可以在GHCI與:kind/:k命令看到:

> :k Int 
Int :: * 

> :k Maybe 
Maybe :: * -> * 

> :k Either 
Either :: * -> * -> * 

在簽名像Maybe a -> Maybe aa是,當你調用函數獲取與特定類型的更換類型變量。 (隱式,這意味着forall a. Maybe a -> Maybe a,你可以寫自己,如果你能擴展,例如ExplicitForallScopedTypeVariables。)

所以,如果你在一個Maybe Intf :: Maybe a -> Maybe a,然後f具有在該呼叫的網站類型Maybe Int -> Maybe Int,因爲a有已被實例化爲Int

編譯器會拒絕Maybe Int a,因爲您只在接受一個參數時向Maybe提供了兩個參數。 (a不是參數的名稱,而是類型的參數。)同樣,它拒絕Maybe -> Maybe,因爲您沒有給出Maybe沒有參數,所以您試圖將兩種類型的* -> *傳遞給函數箭頭構造函數(->),這需要的那種*參數:

> :k (->) 
(->) :: * -> * -> * 

順便說一句,有可能寫出像Maybe -> Maybe並將其擴展到Maybe a -> Maybe a,並且有時這可能是有用的,但它幾乎肯定不是你」現在打算做。

{-# LANGUAGE RankNTypes #-} 
{-# LANGUAGE TypeOperators #-} 

type (~>) f g = forall a. f a -> g a 

f :: Maybe ~> Maybe 
f x = x 

這裏,類型同義詞Maybe ~> Maybe擴展到forall a. Maybe a -> Maybe a,可縮寫爲Maybe a -> Maybe a,你以前寫的簽名。