我之所以选择使用Haskell是因为它的富型系统。这给了我更多的信息在编译时,我的程序,帮助我有信心,它是健全的。
此外,看来Haskell是接近表达问题的最佳语言,如Haskell打字机可以按返回类型分派。。(与Clojure协议相反-它只能在第一个参数上发送)。
当我探索像read这样的Haskell多态返回值函数时
read :: (Read a) => String -> a有以下程序:
addFive :: Int -> Int
addFive x = x + 5
main :: IO ()
main = do
print (addFive (read "11"))
putStrLn (read "11")我得到了以下结果
Runtime error
...
prog: Prelude.read: no parse因此,在具有高级类型系统的语言中,我似乎得到了一个运行时错误。
将此与Clojure中的等效代码进行对比:
(defn add-five [x] (+ 5 x))
(println (add-five (read-string "11")))
(println (read-string "11"))这给了以下结果
16
11我的问题是,为什么返回类型多态性中的Haskell推断类型会导致运行时错误?不应该在编译时提取它们吗?
发布于 2014-04-26 06:29:15
read的类型是
(Read a) => String -> a这意味着它(实际上是编译器或解释器)将根据上下文的要求选择其返回类型。
因此,在addFive (read "11")中,由于addFive需要一个Int,编译器选择的read类型将是String -> Int;在putStrLn (read "11")中,将是String->String,因为putStrLn需要String。
这种选择发生在编译时,这意味着编译之后,程序就等于
main = do
print (addFive (readInt "11"))
putStrLn (readString "11")但是这个readString不能将其参数"11"解析为字符串,因此它在运行时崩溃。
解决这个问题的方法很简单:
main = do
print (addFive (read "11"))
putStrLn (read "\"11\"")发布于 2014-04-26 06:13:22
运行时错误与多态无关,这与字符串"11"不能被read函数解析为字符列表这一事实有关。
这里有一些有用的东西。请注意,"11"可以在运行时被解析为Int,而"\"Some More String\""可以在运行时被解析为字符串。
print $ 5 + read "11"
print $ "Some string" ++ read "\"Some More String\""以下是一些不起作用的事情。它们不能工作,因为"Not an integer"不能被解析为Int,而"11"不能被解析为字符串。
print $ 5 + read "Not an integer"
print $ "Some string" ++ read "11"正如在回答你之前的问题中指出的,类型信息已经在编译时被推断出来了。已经选择了read函数。想象一下,如果我们有两个函数,readInt :: String -> Int和readString :: String -> String分别为Int和String的Read实例提供了read函数。编译器在编译时已经用原始的相应函数替换了read的出现:
print $ 5 + readInt "Not an integer"
print $ "Some string" ++ readString "11"这肯定是在编译时发生的,因为类型信息在编译时被删除了,正如对前面问题的回答所解释的那样。
发布于 2014-04-26 08:38:14
这里问题的一部分是,在Haskell中,可以定义部分函数,即在某些输入上可能失败的函数。例如read,head,tail。非穷尽模式匹配是这种偏好的常见原因,其他原因包括error、undefined和无限递归(显然,在这种情况下没有得到运行时错误)。
特别是,read有点麻烦,因为它要求您确保字符串可以被解析。例如,这通常比确保列表不为空要困难。人们应该使用更安全的变体,例如
readMaybe :: Read a => String -> Maybe a
main = do
print $ readMaybe "11" :: Maybe Int -- prints Just 11
print $ readMaybe "11" :: Maybe String -- prints Nothing问题的另一部分是多态值(如read "11")实际上是伪装的函数,因为它们依赖于计算它们的类型,如上面的示例所示。单态限制是使它们更多地表现为非函数的一种尝试:它迫使编译器为多态值的所有用途找到单一类型。如果可能的话,多态值只能在该类型下计算,并且结果可以在所有的使用中共享。否则,您将得到一个类型错误,即使代码可以在没有限制的情况下进行键入。
例如,下面的代码
main = do
let x = readMaybe "11"
print $ x :: Maybe Int
print $ x :: Maybe Int如果单态限制打开,则解析11一次,关闭时解析两次(除非编译器足够聪明地进行一些优化)。相比之下,
main = do
let x = readMaybe "11"
print $ x :: Maybe Int
print $ x :: Maybe String如果单态限制打开,则引发编译时类型错误,如果其关闭,则编译和运行良好(打印“仅11”和“无”)。
因此,在启用和禁用限制之间没有明显的赢家。
https://stackoverflow.com/questions/23307252
复制相似问题