为什么总和xy是类型(数字a)=> a

我一直在阅读关于Haskell的内容,我很难理解函数定义是如何在这种语言中处理的。

假设我正在定义一个sum函数:

let sum x y = x + y

如果我查询Haskell的类型

:t sum

我明白了

sum :: (Num a) => a -> a -> a
  • 这是什么意思=>运营商? 它与lambda表达式有什么关系? 这就是C#中的一种信号,即=>运算符后面的内容是一个。
  • a -> a -> a是什么意思? 通过对一些不同功能的检查,我一直在尝试,似乎最初的a -> a是参数,最后-> a是sum函数的结果。 如果这是正确的,为什么不是(a, a) -> a ,这看起来更直观?

  • 0. Haskell =>与C#的=>无关。 在Haskell中,创建一个匿名函数

    x -> x * x
    

    此外,不要命名函数sum因为Prelude中已经存在这样的函数。 为了避免混淆,我们从现在开始plus它。

    1.无论如何,Haskell中的=>为该类型提供了一个上下文。 例如:

    show :: (Show a) => a -> String
    

    在此, Show a =>a类型的类型必须为类的实例Show ,这意味着a必须能够转换为字符串。 类似地, (Num a) => a -> a -> a装置的a类型必须是类型次数,这意味着的实例a必须是像一个数字。 这会对a show一个约束,以便showplus不会接受某些不受支持的输入,例如plus "56" "abc" 。 (字符串不像一个数字。)

    类型类与C#的接口类似,或者更具体地说,泛型中的接口基类型约束。 查看问题在Haskell中解释类型以获取更多信息。

    2. a -> a -> a表示a -> (a -> a) 。 因此,它实际上是返回另一个函数的一元函数。

    plus x = y -> x + y
    

    这使得部分应用(柯里化)非常容易。 部分应用程序被大量使用。 当使用高阶函数时。 比如我们可以使用

    map (plus 4) [1,2,3,4]
    

    将4添加到列表的每个元素。 事实上,我们可以再次使用部分应用程序来定义:

    plusFourToList :: Num a => [a] -> [a]
    plusFourToList = map (plus 4)
    

    如果一个函数默认以(a,b,c,...)->z的形式写入,我们将不得不引入很多lambda表达式:

    plusFourToList = l -> map(y -> plus(4,y), l) 
    

    这是因为

    Haskell中的每个函数都有一个参数并返回一个值

    如果一个函数需要多个值,那么该函数将是一个curried函数,或者它必须采用一个元组。

    如果我们添加一个括号,函数签名变成:

    sum :: (Num a) => a -> (a -> a)
    

    在Haskell中,函数签名: A -> B表示函数的“域”为A ,函数的“Codomain”为B的函数; 或者在程序员的语言中,函数接受A类型的参数并返回类型B的值。

    因此,函数定义sum :: Num -> (Num -> Num)表示sum是“一个函数,它接受一个类型a a的参数并返回一个类型为Num -> Num的函数。

    实际上,这导致了咖啡/部分功能。

    在Haskell这样的函数式语言中,currying的概念是必不可少的,因为你会想要做如下的事情:

    map (sum 5) [1, 2, 3, 5, 3, 1, 3, 4]  -- note: it is usually better to use (+ 5)
    

    在该代码中,(总和5)是一个接受单个参数的函数,该函数(总和5)将针对列表中的每个项目被调用,例如((sum5)1)返回6。

    如果sum具有sum :: (Num, Num) -> Num的签名,则sum将同时接收其两个参数,因为现在sum是一个“接收tuple (Num, Num)并返回一个数字“。

    现在,第二个问题, Num a => a -> a是什么意思? 它基本上是一个简短的说法,每次你看到a签名时,用Num或其派生类替换它。


    Num a =>意思是“在下面, a应该指的是一个类型,它是类型类型Num一个实例”(它有点像数字类型的接口)。

    =>运算符将“类型类型约束”从类型的“主体”中分离出来。 它有点像C#中通用约束的where运算符。 你可以把它看作是一个逻辑蕴涵,比如“如果a是一个数字类型,那么sum可以用于类型a -> a -> a ”。

    a -> a -> a装置“其采用一个功能a ,并返回一个函数,它接受一个a并返回a ”。 为了有意义,你需要明白sum xy解析为(sum x) y

    换句话说:你首先用参数x调用sum 。 然后您返回一个新类型a -> a函数。 然后你用参数y调用该函数,现在你得到一个类型a a的函数,其中axy的类型,并且必须是Num类型类型的实例。

    如果你想sum类型为Num a => (a,a) -> a ,你可以将它定义为sum (x,y) = x+y 。 在这种情况下,您有一个函数,它接受一个包含两个a的元组并返回一个a (其中a又是Num类型类的实例)。

    然而,“咖喱风格”(函数返回函数来模拟多个参数)比元组风格更常用,因为它允许您轻松地部分应用函数。 示例map (sum 5) [1,2,3] 。 如果您已经定义sum使用一个元组,你必须做的map (y -> sum 5 y) [1,2,3]

    链接地址: http://www.djcxy.com/p/80787.html

    上一篇: Why sum x y is of type (Num a) => a

    下一篇: Real World Functional Programming in Scala