Where 子句中的类型说明

Type Specification in a Where Clause

作为家庭作业的一部分,我正在尝试做一些非常简单的事情。我需要做的就是编写一个函数,该函数接收表示三角形底长和高长的二元组数字列表,以及 return 与这些三角形对应的区域列表。其中一个要求是我通过定义一个函数并在 where 子句中声明其类型来实现这一点。到目前为止我尝试过的所有东西都无法编译,这就是我得到的:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: (Num, Num) -> Num --this uses 4 preceding spaces 
triArea (base, height) = base*height/2

这失败并出现错误 The type signature for ‘triArea’ lacks an accompanying binding,对我来说这听起来像是没有在 where 子句中定义 triArea。好的,让我们缩进它以匹配 where:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: (Num, Num) -> Num --this uses 4 preceding spaces 
    triArea (base, height) = base*height/2 --... and so does this

这个无法编译特别没有信息的错误消息 parse error on input triArea。只是为了好玩,让我们尝试将它缩进一点,因为我不知道还能做什么:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: (Num, Num) -> Num --this uses 4 preceding spaces 
        triArea (base, height) = base*height/2 --this has 8

但是,没有骰子,失败并显示相同的 parse error 消息。我尝试用等效的 4-space 制表符替换其中每一个中的间距,但那没有 帮助。前两个使用制表符产生与 spaces 相同的错误,但最后一个产生的错误如下所示:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: (Num, Num) -> Num --this uses a preceding tab character 
        triArea (base, height) = base*height/2 --this has 2

给出错误信息

Illegal type signature: ‘(Num, Num) -> Num triArea (base, height)’
  Perhaps you intended to use ScopedTypeVariables
In a pattern type-signature

我不知道那是什么意思,但它似乎突然忽略了换行符。我一直在通读 "Learn You a Haskell",我应该能够使用前三章中提供的信息来做到这一点,但我已经搜索了那些并且他们从未指定在中定义的功能类型这些章节中的 where 子句。作为记录,他们的例子似乎不尊重间距,我复制了其中一个的风格:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: (Num, Num) -> Num --4 preceding spaces
          triArea (base, height) = base*height/2 --10 preceding spaces

但这也无法编译,吐出完全无法理解的错误消息:

Expecting one more argument to ‘Num’
    The first argument of a tuple should have kind ‘*’,
      but ‘Num’ has kind ‘* -> GHC.Prim.Constraint’
    In the type signature for ‘triArea’: triArea :: (Num, Num) -> Num
    In an equation for ‘calcTriangleAreas’:
        calcTriangleAreas xs
          = [triArea x | x <- xs]
          where
              triArea :: (Num, Num) -> Num
              triArea (base, height) = base * height / 2

当我 google/hoogle 时,我找不到任何东西,而且我已经查看了 this question,但它不仅显示 haskell 太远了 高级供我阅读,但根据内容,我认为他们不会遇到与我相同的问题。我已经尝试指定 calcTriangleAreas 的类型,并且我已经尝试将 triArea 规范中的类型别名为 Floating,坦率地说,我已经无能为力了。我的文件的第一行是 module ChapterThree where,但除此之外,我在每个示例中显示的代码都是整个文件。

我正在使用 32 位 Linux Mint 18,我正在使用 ghc ChapterThree.hs Chapter3UnitTests.hs -o Test 进行编译,其中 ChapterThree.hs 是我的文件,单元测试由我提供老师,所以我可以很容易地判断我的程序是否有效(它永远不会进入 ChapterThreeUnitTests.hs 的编译步骤,所以我认为内容不重要),我的 ghc 版本是 7.10.3.

编辑:请注意,如果我完全删除类型规范,一切都可以正常编译,并且该函数通过所有相关的单元测试。

求求你,救救我吧。

你最后的例子是正确的,但是你写的类型没有意义。 Numclass 约束 而不是类型。你可能想写:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: Num a => (a, a) -> a
          triArea (base, height) = base*height/2 

规则是:分配必须对齐。

此外 (/) 需要 Fractional class:

calcTriangleAreas xs = [triArea x | x<-xs]
    where triArea:: Fractional a => (a, a) -> a
          triArea (base, height) = base*height/2 

请注意,缩进级别与 where 的缩进级别 没有任何关系。例如,您可以这样编写代码:

calcTriangleAreas xs = [triArea x | x<-xs] where
    triArea:: Fractional a => (a, a) -> a
    triArea (base, height) = base*height/2 

缩进级别由 where/let 中的第一个赋值或 do 块的第一行定义。所有其他行必须与该行对齐。

所以这些都是正确的:

f x = y where
  a = b
  y = ...

f x = y
  where a = b
        y = ...

f x = y
  where
    a = b
    y = ...

spitting out the utterly incomprehensible error message:

Expecting one more argument to ‘Num’
   The first argument of a tuple should have kind ‘*’,
     but ‘Num’ has kind ‘* -> GHC.Prim.Constraint’

为了补充 Bakuriu 的回答,让我为您解码。

错误说 -- 逐行:

  • Num 还需要一个参数——我们应该从一些 a
  • 写成 Num a
  • (,) 这样的元组类型需要一个类型作为参数。语句 "should have kind *" 表示 "should be a type"。 Haskell 的分类系统将 * 关联为 "kind of types"。我们有例如Int :: *String :: *(Maybe Char, [Int]) :: *Maybe[] 等一元类型构造函数不是类型,而是从类型到类型的函数。我们写 Maybe :: *->*[] :: *->*。它们的种类 *->* 使得可以声明,由于 Maybe :: *->*Char :: *,我们有类似于普通值级函数的 Maybe Char :: * ("is a type")。 pair 类型构造函数具有种类 (,) :: *->*->*:它需要两种类型并提供一种类型。
  • Num 种类 *-> Constraint。这意味着,对于每个类型 TNum T 的种类将是 Constraint,而不是 (,) 预期的 *。这会触发一种错误。种类 Constraint 被赋予类型类约束,例如 Eq IntOrd BoolNum Int。这些不是类型,而是对类型的要求。当我们使用 (+) :: Num a => a->a->a 时,我们看到 (+) 适用于任何类型 a,只要该类型满足 Num a,即是数字。由于 Num T 不是类型,我们不能写 Maybe (Num T)[Num T],我们只能写例如Maybe a 并要求 a 属于类型类 Num.