Haskell 理解函数类型

Haskell 理解函数类型,haskell,functional-programming,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] ] 我脑子里出现

在试图理解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] ]   
我脑子里出现了几个问题,我自己很难解决这些问题:

  • 为什么boolFcn的类型由
    a,a1
    文字组成,而三角形的类型由
    t,t1
    文字组成?
    a
    t
    之间有什么区别吗
  • 为什么boolFcn的类型不能简化为:

    (数值a,等式a)=>a->a->Bool

  • a
    a1
    具有相同的类型类,那么为什么我不能简单地使用一个
    a
    来编写它们呢?当我检查函数的类型时:

    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
    
    如果这个问题很琐碎,我很抱歉,不过我很乐意听到关于这个问题的任何解释/提示

  • 是的,
    a
    t
    在这些示例中基本相同。这种差异只是类型推断算法的一个副作用

  • 好的,在
    boolFcn
    中,
    (Num a,Eq a)=>a->a->Bool
    不够一般,因为前两个参数不需要是相同的类型。考虑下面的调用:

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

    这是有效的,因为
    Int
    Double
    都是
    Num
    Eq
    类型类的成员,但它们显然不是同一类型。在您的
    sumthis
    示例中,
    x
    y
    必须是相同的类型,因为它们被用作需要相同类型参数的函数的输入

    我们可以通过检查
    :t(+)
    ,它返回
    (+)::numa=>a->a->a
    。由于
    +
    的参数必须是相同的类型,
    x
    y
    必须是相同的类型,因此
    sumthis
    必须需要相同类型的参数。所以

    sumthis(4::Int)(4::Double)

    这是无效的


  • 首先,类型中的变量名并不重要。它们是任意命名的,但给定类型的事物将是相同的。例如,
    a->b->b=/=a->b->a
    ,但
    a->b==c->d
    (只要它们不显示在同一类型签名中!)。typechecker动态地为类型变量提供多态值名称,因此不必担心命名方案中的细微差异:它们并不重要。它们只是变量

    至于typechecker如何实际确定表达式的类型,让我们看一下第一个示例:

    (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
    上,但是
    y
    x
    除了使用
    (&&&)
    之外,没有交互作用。这说明
    x
    y
    不需要是相同的类型,因此标题中的类型变量不同。最后,
    (&&&
    返回一个
    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] ]   
    

    a
    b
    c
    现在需要具有相同的类型,因此签名将更改为
    三角形::(Num t,Enum t)=>[[t]]

    如果它们不是相同的类型,为什么不坚持使用(Num a,Eq a,Num b,Eq b)=>a->b->Bool?为什么有时有t1,有时有a1,有时只有b?@Niemand,我不能回答这个问题,但这并不重要。如果您给出自己的类型签名,您可以随意命名类型变量(只要它们以小写字母开头)。如果你不给出自己的类型签名,编译器会编造一些名称,它并不在乎你是否喜欢它们。@Niemand我认为通常的惯例似乎是使用
    a
    a1
    来表示属于同一类型类的“相关”类型,比如在你的
    boolFcn
    ,以及
    a
    b
    “无关的类型,如
    Int
    String
    。至于为什么有时是
    a->a1
    ,有时是
    t->t1
    ,我真的不知道。@Niemand你可能真的想提出一个新问题,特别是关于GHC的类型推断如何选择它的类型变量,因为我在谷歌上找不到太多。@JeffBurka Burka按照你的建议-。不过,谢谢你的重播,这里有一张投票给你:)。
    (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] ]   
    
    triangles = [ [a,b,c] | c <- [1..10], b <- [1..10], a <- [1..10] ]