我之所以选择使用haskell,是因为它有丰富的类型系统。这在编译时为我提供了关于程序的更多信息,帮助我相信它是合理的。
此外,似乎Haskell是处理表达式问题的最佳语言,因为Haskell类型类可以在返回类型上进行分派。(与clojure协议相反-只能在第一个参数上调度)。
当我探索haskell多态性返回值函数(如read时:
使用以下程序:
号
我得到以下结果:
所以我似乎在使用高级类型系统的语言中得到了一个运行时错误。
对比一下Clojure中的等效代码:
1 2 3 4
| (defn add-five [x] (+ 5 x))
(println (add-five (read-string"11")))
(println (read-string"11")) |
。
结果如下:
我的问题是,为什么Haskell在返回类型多态性中推断类型会导致运行时错误?它不应该在编译时提取它们吗?
- 这与您之前的问题密切相关:stackoverflow.com/questions/23282921/…
- 简单地说(没有足够的时间来写一个完整的答案):因为试图将"11"作为字符串读取失败。问题本身与类型无关,只是(read :: String -> String)"11"会崩溃;用于读取的字符串格式是文字haskell字符串,例如""11""的格式。
- @我和Cirdec相信这个问题在那个问题上得到了解答。
- 谢谢你,安塔尔-你能扩大范围吗?(在回答中?)
- 对不起,安塔尔-当我自己尝试的时候,我仍然会得到同样的错误:ideone.com/udjggz
- @霍克耶,你不应该改变第一个read的论点,只应该改变第二个read的论点。
- 找到了ideone.com/oegjmf
- 这些代码片段不是等价的。返回并将print放在haskell代码的两个地方,您将得到所需的编译时错误(类型不明确)。实际上,您正在强制haskell解析一个没有字符串的字符串。Clojure正在分析一个数字并打印该数字的表示。
- 正如我在回答中所说,你需要后退一步。您的问题继续表明对类型错误和其他错误之间的区别有一个基本的误解。
- 谢谢A.Webb-复制于:ideone.com/zi0wcv
运行时错误与多态性无关,所有的事情都与字符串"11"不能被read函数解析为字符列表这一事实有关。
这里有一些有用的东西。注意,"11"可以在运行时被解析为Int和"\"Some More String\""可以在运行时被解析为字符串。
以下是一些不起作用的东西。它们不起作用,因为"Not an integer"不能被解析为Int,"11"不能被解析为字符串。
号
正如前面问题的答案中指出的,类型信息已经在编译时被推断出来了。已经选择了read功能。假设我们有两个函数readInt :: String -> Int和readString :: String -> String,分别为read函数和String实例提供read函数。在编译时,编译器已将出现的read替换为原始的各自函数:
1 2
| print $ 5 + readInt "Not an integer"
print $"Some string" ++ readString "11" |
这一定是在编译时发生的,因为类型信息在编译时被消除,正如前面问题的答案中所解释的那样。
这里的一个问题是,在haskell中,可以定义部分函数,即在某些输入上可能失败的函数。例如read、head、tail。非穷尽模式匹配是造成这种偏袒的常见原因,其他原因包括error、undefined和无限递归(即使在这种情况下,显然不会出现运行时错误)。
特别是,read有点讨厌,因为它要求您确保可以解析字符串。例如,这通常比确保列表不为空要困难。应该使用更安全的变体,如
。
问题的另一部分是,多态值(如read"11")实际上是伪装的函数,因为它们取决于它们被评估的类型,如上例所示。单态限制试图使它们更像非函数:它强制编译器为多态值的所有使用找到一个单一的类型。如果可能的话,多态性值只在该类型上进行评估,并且结果可以在所有用途中共享。否则,您会得到一个类型错误,即使代码可以在没有限制的情况下被类型化。
例如,以下代码
如果打开了单态限制,则解析一次11,如果关闭了,则解析两次(除非编译器足够聪明,可以进行一些优化)。相比之下,
。
如果启用了单态限制,则引发编译时类型错误;如果禁用了限制,则编译并运行正常(打印"just 11"和"nothing")。
因此,在启用和禁用限制之间没有明确的赢家。
read的类型是
。
这意味着它(实际上是编译器或解释器)将根据上下文的要求选择其返回类型。
因此,在addFive (read"11")中,由于addFive需要Int,编译器选择的read类型为String -> Int;在putStrLn (read"11")中,由于putStrLn需要String,所以String->String。
这个选择发生在编译时,也就是说在编译之后,你的程序
1 2 3
| main = do
print (addFive (readInt "11"))
putStrLn (readString "11") |
但是这个readString不能把它的参数"11"解析为字符串,所以它在运行时崩溃。
解决这个问题很简单:
。
- 好的-返回类型多态性优先于参数多态调度吗?
- @我不明白你的问题。你能举例说明一下这个问题吗?也许在另一个问题上。
- @这个问题与类型多态性无关。这个问题就像试图把"abc"解析成一个十进制数字。编译器无法判断"abc"不会作为数字进行解析。在haskell中,putStrLn (read"11")希望"11"代表String,它总是包括引号。它与show相反:show"11"是['"','1','1','"'],加上引号,而不仅仅是['1','1']。所以read ['1','1']不能解析String,建议解析read ['"','1','1','"']。
- @Hawkeye的各种多态性机制是相同的。它们必须同时且唯一地对齐,否则类型检查失败。