定义具有可能签名的haskell函数

jll*_*jll 1 haskell

我不清楚如何在haskell中编写函数签名,尤其是使用Maybe.考虑:

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

main = print (f (Just 5))
Run Code Online (Sandbox Code Playgroud)

这有效,但为什么函数签名不能这样呢?

f :: Maybe -> Maybe 
Run Code Online (Sandbox Code Playgroud)

因为f只需要一个Maybe类型并返回一个Maybe类型.

相关:如果我想让Maybe类型更具体并且是a Maybe Int,为什么这不起作用?

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

main = print (f (Just (Int 5)))
Run Code Online (Sandbox Code Playgroud)

(我正在运行所有代码runhaskell test.hs)

luq*_*qui 10

好像你对类型变量感到困惑.首先,在

f :: Maybe a -> Maybe a
f = \a -> a
Run Code Online (Sandbox Code Playgroud)

a的第一行有什么做的a的第二行,我们可以这样写:

f :: Maybe a -> Maybe a
f = \x -> x
Run Code Online (Sandbox Code Playgroud)

甚至

f :: Maybe foo -> Maybe foo
f = \bar -> bar
Run Code Online (Sandbox Code Playgroud)

a是代表类型的变量.所以f这里声明f一次有很多类型:

f :: Maybe Int -> Maybe Int
f :: Maybe String -> Maybe String
f :: Maybe (Maybe Bool) -> Maybe (Maybe Bool)
...
Run Code Online (Sandbox Code Playgroud)

等等.正如我怀疑你所想的那样,这不是一些"标记"的论点.两者a相同的事实意味着参数类型必须是相同的结果类型.如果我们说过f :: Maybe a -> Maybe b我们会得到这个家庭:

f :: Maybe Int -> Maybe Bool
f :: Maybe String -> Maybe String
f :: Maybe (Maybe Bool) -> Maybe Int
...
Run Code Online (Sandbox Code Playgroud)

也就是说,a并且b现在可以代表不同的类型,但参数和结果还是要Maybe.

你不能说的原因

f :: Maybe -> Maybe
Run Code Online (Sandbox Code Playgroud)

因为Maybe它不是一个类型 - 它是一个类型构造函数.如果你给它一个类型,它会给你一个类型.所以Maybe Int并且Maybe String是类型,并且通常Maybe a是类型,只要a是类型.

Maybe Int a(已解析(Maybe Int) a)没有意义,因为Maybe Int它不是一个类型构造函数 - 它不再接受任何参数.

推荐阅读:LYAH的类型和类型.


Jon*_*rdy 5

Maybe是一个类型构造函数,本质上是一个类型级函数.它需要一个类型(例如Int)并返回一个类型(例如Maybe Int).该类型的"类型"被称为:种有值的类型,比如Int,被称为*.采用一个参数的类型构造函数的类型是* -> *.你可以使用:kind/ :k命令在GHCi中看到这个:

> :k Int
Int :: *

> :k Maybe
Maybe :: * -> *

> :k Either
Either :: * -> * -> *
Run Code Online (Sandbox Code Playgroud)

在签名中Maybe a -> Maybe a,a是一个类型变量,在调用函数时会被特定类型替换.(隐含地,这意味着forall a. Maybe a -> Maybe a,如果您启用诸如ExplicitForall或之类的扩展,您可以自己编写ScopedTypeVariables.)

因此,如果你调用f :: Maybe a -> Maybe aa Maybe Int,那么在该调用站点上f有类型Maybe Int -> Maybe Int,因为a已经实例化Int.

编译器拒绝,Maybe Int a因为您Maybe只提供两个参数,只接受一个参数.(这a不是参数的名称,而是类型的参数.)同样,它拒绝Maybe -> Maybe因为你没有给出Maybe参数,所以你试图将两种类型的类型* -> *传递给函数箭头构造函数(->),它接受参数善良的*:

> :k (->)
(->) :: * -> * -> *
Run Code Online (Sandbox Code Playgroud)

顺便说一句,有可能写出类似的内容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
Run Code Online (Sandbox Code Playgroud)

在这里,类型同义词Maybe ~> Maybe扩展为forall a. Maybe a -> Maybe a,您可以缩写为Maybe a -> Maybe a您之前编写的签名.