2017-07-28 65 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,你以前写的签名。