理解这个Haskell函数的类型

时间:2017-02-05 13:21:36

标签: haskell

我是Haskell的新手,我对以下代码有疑问:

a x (b:bs) = a (b x) bs
a x b = x []

这些模式的一般类型是什么?

使用:info,我得到:([t1] -> t) -> [([t1] -> t) -> [t1] -> t] -> t

然而,我无法理解为什么。 a只有两个"输入"或不?

2 个答案:

答案 0 :(得分:6)

  

我只有两个"输入"不是吗?

是的,你有两个参数(技术上一个是因为currying的工作方式)。

  

使用:info a I get:([t1] -> t) -> [([t1] -> t) -> [t1] -> t] -> t

这意味着第一个参数的类型为[t1] -> t(因此它是一个获取t1列表并生成t的函数),第二个参数的类型为{ {1}}(采用[([t1] -> t) -> [t1] -> t]类型函数和[t1] -> t列表并生成t1)的函数列表,结果类型为t

  

但我无法理解为什么。

让我们看一下代码:

t

第一个参数只是一个变量模式,所以它的类型是任何东西。第二个是列表模式,因此它必须是一个列表。

因此,如果我们使用带编号的问号来表示我们还不知道的类型,我们到目前为止知道以下内容:

a x (b:bs) =

现在让我们看一下身体:

x :: ?1
b :: ?2
bs :: [?2]
a :: ?1 -> [?2] -> ?3

a (b x) bs 已应用于b,因此x必须是以b类型为参数的函数。 x的结果用作b x的第一个参数,因此a的结果类型也必须与b的类型匹配。因此,x必须为?2,我们会:

?1 -> ?1

现在让我们来看看第二个正文(x :: ?1 b :: ?1 -> ?1 bs :: [?1 -> ?1] a :: ?1 -> [?1 -> ?1] -> ?3 b现在已经不在范围内了,技术上还有新的bs,但是&{ #39; s未使用,因此我们将忽略它):

b

此处x [] 应用于空列表。所以它也必须是一个函数,它的参数类型是某种列表。由于x的结果也是x []的结果,a的结果类型必须是x。所以我们得到?3,因此:

?1 = [?4] -> ?3

由于a :: ([?4] -> ?3) -> [([?4] -> ?3) -> ([?4] -> ?3)] -> ?3 是右关联的,我们可以在那里删除括号并得到:

->

现在,由于我们拥有的所有类型信息,剩下的所有问号都是实际的类型变量,因此我们可以用任意类型的变量名替换它们。如果我们选择a :: ([?4] -> ?3) -> [([?4] -> ?3) -> [?4] -> ?3] -> ?3 ?3 = t,我们会得到?4 = t1的确切输出(但我们当然可以将其他任何名称命名,类型仍然相同)。

答案 1 :(得分:1)

如果你只看一下定义的前半部分,我们得到一个很好的类型:

import shapeless._, nat._, ops.hlist._, ops.nat._

trait SumEq5[A]
object SumEq5 {
  def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev

  implicit def sumEq5Ev[L1 <: HList, L2 <: HList, L3 <: HList, A <: Nat, B <: Nat](
    implicit hcons1: IsHCons.Aux[L1, A, L2],
             hcons2: IsHCons.Aux[L2, B, L3],
             ev: Sum.Aux[A, B, _5]
  ): SumEq5[L1] = new SumEq5[L1] {}
}

object T {
  def main(args: Array[String]): Unit = {
    SumEq5[_0 :: _5 :: HNil]
  }
}

我们取一个值并从列表中应用一堆函数。我们还不知道在用完函数后会发生什么,因为我们还没有查看第二部分。

让我们先想象另一个结局:

a :: t -> [t -> t] -> t1
a x (b:bs) = a (b x) bs

当我们完成后,我们返回最终值。请注意,这解决了我们不确定的返回类型由于我们只是在列表上执行反向函数应用程序,我们可以在列表中使用反向函数应用程序进行折叠并获得相同的函数:

a :: t -> [t -> t] -> t
a x [] = x

但这不是我们的功能在现实中如何结束。它以

结尾
a :: t -> [t -> t] -> t
a = foldl (flip ($))

以下是我们刚刚学到的内容:a x [] = x [] 是一个函数,它接受一些事物列表并返回一些东西。我们无法说出这些事情,请致电xt1。我们知道t2的类型为x,因此我们可以使用t替换第一个函数中出现的所有t

[t1] -> t2

这就是那种签名发生的方式!我们也知道它相当于:

a :: ([t1] -> t2) -> [([t1] -> t2) -> ([t1] -> t2)] -> t2

仍然是一个奇怪的功能。