Haskell的新手很抱歉,如果这是非常基本的
此示例取自“Real World Haskell” -
ghci> :type fst
fst :: (a, b) -> a
它们显示fst
函数的类型,然后按照此段落进行...
“fst
的结果类型为 a
。我们已经提到参数多态使真实类型无法访问:fst
没有足够的信息来构建 a
类型的值,也不能将 a
转换为 b
因此,唯一可行的有效行为(省略无限循环或崩溃)可以返回该对的第一个元素。“
我觉得我错过了段落的基本点,也许是Haskell的重要内容。为什么fst
函数无法返回类型 b
?为什么不能将元组作为参数,只需返回Int
(或任何其他类型 a
)?我不明白为什么它必须返回类型 a
?
由于
答案 0 :(得分:24)
如果它做了这些事情,它的类型会改变。引用的内容是,鉴于我们知道fst
属于(a, b) -> a
类型,我们可以对其进行推断。如果它有不同的类型,我们将无法这样做。
例如,请参阅
snd :: (a, b) -> a
snd (x, y) = y
不进行类型检查,因此我们知道类型(a, b) -> a
的值不能像snd
那样。
参数化基本上是这样一个事实,即某种类型的多态函数必须遵守构造的某些定律 - 即,没有那种不遵守它们的类型良好的表达式。因此,为了能够用它来证明fst
的内容,我们必须首先知道fst
的类型。
特别注意多态这个词:我们不能对非多态类型做同样的推论。例如,
myFst :: (Int, String) -> Int
myFst (a, b) = a
类型检查,但
也是如此myFst :: (Int, String) -> Int
myFst (a, b) = 42
甚至
myFst :: (Int, String) -> Int
myFst (a, b) = length b
参数化关键在于多态函数无法“查看”它所调用的类型这一事实。因此,fst
知道的 a 类型的唯一值是它给出的值:元组的第一个元素。
答案 1 :(得分:5)
重点是,一旦拥有该类型,实施选项就会受到很大限制。如果您返回Int
,那么您的类型将为(a,b) -> Int
。由于a
可以是任何东西,我们无法在实施过程中凭借undefined
来解决问题,因此必须返回由来电者提供给我们的那个。
答案 2 :(得分:4)
您应该阅读Theorems for Free文章。
答案 3 :(得分:3)
让我们尝试为Real World Haskell已经提供的更多手势。让我们试着说服自己,假设我们有一个类型为fst
的函数(a,b) -> a
,它的唯一总函数就是以下函数:
fst (x,y) = x
首先,除了a
类型的值之外,我们不能返回任何其他内容,前提是fst的类型为(a,b) -> a
,因此我们不能拥有fst (x,y) = y
或{{ 1}}因为它没有正确的类型。
现在,正如RWH所说,如果我给fst (x,y) = 1
fst
,(Int,Int)
不知道这些是Ints,那么fst
或a
不需要属于任何类型类,因此b
没有与fst
或a
相关联的可用值或函数。
所以b
只知道我提供的fst
值和a
值,而我无法将b
变成b
(它无法生成a
函数,因此必须返回给定的b -> a
值。
这实际上并不仅仅是神奇的挥手,实际上可以推断出给定多态类型的可能表达式。实际上有一个名为djinn的程序就是这样做的。
答案 4 :(得分:2)
这里的要点是a
和b
都是类型变量(可能是相同的,但不需要)。无论如何,因为对于给定的两个元素的元组,fst
总是返回第一个元素,返回的类型必须始终与第一个元素的类型相同。
答案 5 :(得分:2)
你可能遗漏的基本事情是:
在大多数编程语言中,如果你说“此函数返回任何类型”,则意味着函数可以决定它实际返回的值的类型。
在Haskell中,如果你说“此函数返回任何类型”,则表示调用者决定应该是什么类型。 (!)
所以,如果我写foo :: Int -> x
,它就不能只返回String
,因为我可能不会要求它String
。我可能会要求Customer
或ThreadId
或任何。
显然,foo
无法知道如何创建每种可能类型的值,即使是尚不存在的类型。简而言之,写foo
是不可能的。你尝试的一切都会给你类型错误,而且不会编译。
(警告:有一种方式来执行此操作。foo
可以永久循环,或者抛出异常。但不能返回有效值。 )
函数无法创建任何可能类型的值。但是,完全可能一个函数移动数据而不关心它是什么类型。因此,如果您看到一个接受任何类型数据的函数,那么它唯一能做的就是移动它。
或者,如果类型必须属于特定类,则该函数可以在其上使用该类的方法。 (可以。它没有,但可以,如果它想要的话。)
从根本上说,这就是为什么你只需要通过查看类型签名来实际告诉一个函数做什么。类型签名告诉您函数“知道”它将要给出的数据,以及它可能执行的操作。这就是为什么通过类型签名搜索Haskell函数非常有用。
您已经听说过“在Haskell中,如果它编译,它通常可以正常工作”?嗯,这就是原因。 ; - )