类型为Num([Char] - > t)=>的奇怪Haskell表达式Ť

时间:2015-07-19 17:05:10

标签: haskell type-inference literals ghci

在GHCi中进行一些练习时,我输入并获得以下>

ghci> (1 "one")

<interactive>:187:1:
  No instance for (Num ([Char] -> a0)) arising from a use of ‘it’
  In a stmt of an interactive GHCi command: print it

这是一个错误,但如果我向GHCi询问表达式的类型,它不会给出任何错误:

ghci> :type (1 "one")
(1 "one") :: Num ([Char] -> t) => t

(1 "one")是什么意思?

为什么这个表达式会出错,但是GHCi告诉它输入得好?

Num ([Char] -> t) => t是什么意思?

感谢。

3 个答案:

答案 0 :(得分:10)

哈斯克尔报告救援! (引用section 6.4.1

  

整数文字表示函数fromInteger应用于Integer类型的适当值。

fromInteger的类型为:

Prelude> :t fromInteger
fromInteger :: Num a => Integer -> a

所以1实际上是fromInteger (1 :: Integer)的语法糖。那么你的表达是:

fromInteger 1 "one"

可以写成:

(fromInteger 1) "one"

现在,fromInteger产生一个数字(即类型的值,它是Num的一个实例,正如它的类型告诉我们的那样)。在您的表达式中,此数字将应用于[Char](字符串"one")。 GHC正确地组合了这两条信息,推断出你的表达式有类型:

Num ([Char] -> t) => t

也就是说,将t函数应用于Num的结果(未指定类型[Char])。原则上这是一种有效的类型。唯一的问题是Num没有[Char] -> t的实例(也就是说,带字符串的函数不是数字,这并不奇怪)。

P.S。:正如Sibi和Ørjan指出的那样,在GHC 7.10及更高版本中,如果FlexibleContexts GHC扩展已启用,您将只看到问题中提到的错误;否则类型检查器会抱怨在类约束中有固定类型和类型构造函数(即Char[](->))。

答案 1 :(得分:3)

Haskell是一种非常灵活的语言,但在字面意义上也是一种非常逻辑的语言。通常情况下,在大多数语言中只会出现语法错误的事情,Haskell会查看它们并尝试用它来理解它们,结果确实令人困惑,但实际上只是语言规则的逻辑结果。 / p>

例如,如果我们将您的示例键入Python,它基本上会告诉我们“您输入的内容毫无意义”:

Python 2.7.6 (default, Sep  9 2014, 15:04:36) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> (1 "one")
  File "<stdin>", line 1
    (1 "one")
           ^
SyntaxError: invalid syntax

Ruby做同样的事情:

irb(main):001:0> (1 "one")
SyntaxError: (irb):1: syntax error, unexpected tSTRING_BEG, expecting ')'
(1 "one")
    ^
    from /usr/bin/irb:12:in `<main>'

但是Haskell并没有轻易放弃!它看到(1 "one"),原因是:

  • 表单f x的表达式是函数应用程序,其中f的类型为a -> bx的类型为a并且f x的类型为b
  • 因此,在1 "one"表达式中,1必须是以"one"(a [Char])为参数的函数。

然后给出Haskell对数字文字的处理,它将1转换为fromInteger 1 :: Num b => [Char] -> bfromIntegerNum类的一种方法,这意味着如果您愿意,允许用户为任何类型提供自己的实现,包括[Char] -> b

所以错误信息意味着Haskell,而不是告诉你你输入的内容是无意义的,告诉你你没有教它如何构造一些类型Num b => [Char] -> b,因为这是非常奇怪的事情为了使表达有意义,这需要真实。

答案 2 :(得分:1)

TL; DR:这是一个乱码的无意义类型,不值得担心。

整数文字可以表示实现Num类型类的任何类型的值。因此,1或任何其他整数文字可以在任何需要数字的地方使用。

doubleVal :: Double
doubleVal = 1

intVal :: Int
intVal = 1

integerVal :: Integer
integerVal = 1

这使我们能够在任何数字上下文中灵活地使用整数文字。

当你只使用没有任何类型上下文的整数文字时,ghci不知道它是什么类型。

Prelude> :type 1
1 :: Num a => a

ghci说“'1'是我不知道的某种类型,但我知道无论它是什么类型,该类型都会实现Num类型类。”

Haskell源中每次出现的整数文字都包含一个隐式fromInteger函数。因此,(1 "one")隐式转换为((fromInteger (1::Integer)) "one"),子表达式(fromInteger (1::Integer))具有尚未知的类型Num a => a,同样意味着它是某种未知类型,但我们知道它提供了Num类型类的实例。

我们还可以看到它像"one"一样应用于函数,因此我们知道其类型必须具有[Char] -> a0形式,其中a0是另一种未知类型。因此a[Char] -> a0必须相同。将其替换回我们上面提到的Num a => a类型,我们知道1必须具有类型Num ([Char] -> a0) => [Char] -> a0),而表达式(1 "one")具有类型Num ([Char] -> a0) => a0。将最后一个类型读为“有一些类型a0是将[Char]参数应用于函数的结果,该函数类型是Num类的实例。

因此表达式本身具有有效类型Num ([Char] -> a0) => a0

Haskell有一个叫Monomorphism restriction的东西。其中一个方面是表达式中的所有类型变量必须具有特定的已知类型才能对其进行求值。 GHC在某些情况下使用类型默认规则,以适应单态限制。但是,GHC不知道任何类型a0它可以插入上面定义了Num实例的类型表达式。因此它无法处理它,并为您提供“No Instance for Num ...”消息。