了解功能类型

时间:2014-07-02 15:55:04

标签: haskell functional-programming

我在尝试理解Haskell如何确定函数类型时感到有些困惑。这是一个例子:

boolFcn x y = x/=3 && y/=4

当我检查上述功能的类型时,它给出了结果:

(Num a1, Num a, Eq a1, Eq a) => a -> a1 -> Bool

这是另一个例子:

triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]   

:t triangles上的(Num t2, Num t1, Num t, Enum t2, Enum t1, Enum t) => [(t, t1, t2)] 结果为:

a, a1

我脑子里出现了一些问题,我自己也很难解决问题:

  1. 为什么boolFcn的类型由t,t1文字组成,而三角形的类型由a文字组成? t(Num a, Eq a) => a -> a -> Bool之间有什么区别吗?
  2. 为什么boolFcn的类型不能简化为:

    a

  3. a1a具有相同的类型类别,那么为什么我只能使用一个let sumthis x y = if x > y then x+y else x-y 来编写它们?当我检查功能的类型时:

    (Ord a, Num a) => a -> a -> a
    

    我得到了一个结果:

    (Ord a, Num a, Ord a1, Num a1) => a -> a1 -> a
    

    为什么不导致:

    {{1}}

    对不起,如果这个问题很简单,不过我很乐意听到这个问题的任何解释/提示。

2 个答案:

答案 0 :(得分:6)

  1. 是的,at在这些示例中基本相同。差异只是类型推断算法的副作用。

  2. 嗯,在boolFcn中,(Num a, Eq a) => a -> a -> Bool不够通用,因为前两个参数不一定是同一类型。请考虑以下呼叫:

    boolFcn (4::Int) (4::Double)

    这是有效的,因为IntDouble都是NumEq类型类的成员,但它们显然不是同一类型。在sumthis示例中,xy必须是同一类型,因为它们用作需要相同类型参数的函数的输入。

    我们可以通过选中返回:t (+)的{​​{1}}来查看。由于(+) :: Num a => a -> a -> a的参数必须是同一类型,+x必须是同一类型,因此y必须要求相同类型的参数。因此

    sumthis

    无效。

答案 1 :(得分:4)

首先,类型中的变量名称无关紧要。它们是任意命名的,但给定类型的内容将是相同的。例如,a -> b -> b =/= a -> b -> a,但是a -> b == c -> d(只要它们不会出现在相同类型的签名中!)。类型检查器为变量类型变量提供了多态值,因此不必担心命名方案的细微差别:它们无关紧要。它们只是变量。

关于类型检查器如何实际确定表达式的类型,让我们看看你的第一个例子:

(Num a1, Num a, Eq a1, Eq a) => a -> a1 -> Bool
boolFcn x y = x/=3 && y/=4

首先,类型检查员看到x /= 3。这意味着x必须是Eq,因为(/=)Eq类型类的一部分,而且,它必须是Num,因为3在编译期间被解释为多态Num类型。 y /= 4也会发生同样的情况,但yx(&&)的使用不相互影响。这说明xy不需要是同一类型,因此标题中的类型变量不同。最后,(&&)返回Bool,这就是函数返回的内容。

你的第二个例子因为一些语法糖而有点棘手。

(Num t2, Num t1, Num t, Enum t2, Enum t1, Enum t) =>  [(t, t1, t2)]
triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]   

基本上,当您执行xs = [a..b]之类的操作时,xs的类型必须为Enum a => [a]。直观地说,不可能有一系列无法​​按顺序列举的东西。接下来,这些数字再次被解释为多态Num类型。但为什么我们在签名中有三个不同的类型变量,每个变量都必须是NumEnumNum类型实际上不必相同,因为允许元组(a, b, c)在其第一,第二和第三点中具有不同类型的数据。因此,每个[1..10]可以被解释为不同可枚举数字的列表(即(Int, Integer, Int)(Integer, Integer, Int),并且一切正常。如果你是收集列表而不是元组,如下所示:

triangles = [ [a,b,c] | c <- [1..10], b <- [1..10], a <- [1..10] ]   

abc现在需要具有相同的类型,因此签名更改为triangles :: (Num t, Enum t) => [[t]]