我之前在使用Haskell的read
函数时从String
读取数字时需要指定输出类型,我得知:
read "2" :: Int
除非您执行以下操作:
read "2" + 2
然后,Haskell知道你正在尝试添加,因此它必须是一个数字。
然而,一个特定的功能引起了我的注意,因为通过查看功能我认为它不会编译,但确实如此,我不知道为什么。
这种反向抛光符号计算器实现加法,减法和乘法:
solveRPN :: (Num a, Read a) => String -> a
solveRPN xs = head . foldl foldingFunction [] . words $ xs
where foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (y - x):ys
foldingFunction xs numberString = read numberString:xs
如果你给它一个字符串,如" 2 5 +"它将返回7.
此代码的最后一行是我无法理解的。当你提供这个功能" 2 5 +" xs
列表中的第一个元素是" 2"和累加器的时间是[]
,因此它会滑过前3个模式,最后一个将完成它的工作,因此:
foldingFunction [] "2" = read "2":[]
所以,我的问题是:read "2":[]
怎么会崩溃?如果我试图在控制台中执行这个位,它会给出解析错误,因为read
不知道该字符串应该是什么,对吧?怎么没有(read "2" :: Int):[]
或者什么?
答案 0 :(得分:2)
让我们倒退。
head . foldl foldingFunction [] . words :: Num a => String -> a
foldl foldingFunction [] .words :: Num a => String -> [a]
foldl foldingFunction [] :: Num a => [String] -> [a]
:自foldl ::Foldable t => (b -> a -> b) -> b -> t a -> b
以来,我们可以看到t a ~ [String]
,因此我们也可以看到foldingFunction :: Num a => [a] -> String -> [a]
。
因此,read "2" : [] :: Num a => [a]
与foldingFunction [] "2"
的类型相同。
换句话说,solveRPN
提供了推断read
应返回的必要背景。
答案 1 :(得分:2)
所以你必须要了解的是Haskell在编译时分配所有类型的函数,而不是运行时。此外,这些函数只对其所有模式都有一种类型。
这意味着它将决定整个函数的类型并在每种情况下使用该决策。此外,Haskell做了相当重的类型推断(与大多数其他语言不同),因此有时会根据可能看起来有点远离原始函数调用的内容类型来确定函数的类型。
让我们来看看你的例子:
solveRPN :: (Num a, Read a) => String -> a
solveRPN xs = head . foldl foldingFunction [] . words $ xs
where foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (y - x):ys
foldingFunction xs numberString = read numberString:xs
首先,solveRPN
的类型声明为String -> a
。
现在,看一下solveRPN
的定义,我们在第一行说:
solveRPN xs = head . foldl foldingFunction [] . words $ xs
现在,那里使用的名称类型是:
xs :: String (from the type of solveRPN)
head :: [b] -> b (I'm using different variable names for each different type)
foldl :: (c -> d -> c) -> c -> [d] -> c
words :: String -> [String]
因此,solveRPN
的类型意味着我们必须将该类型b
与类型a
相同,并且因为head
应用于{{1}的输出1}},我们必须使该类型foldl
与类型c
相同。现在,因为[a]
的第三个参数属于foldl
类型,我们知道类型[String]
是d
,现在我们已经足够确定String
的类型}:
foldingFunction
答案 2 :(得分:2)
如果我试图在控制台中执行此位,它会给出解析错误,因为读取不知道该字符串应该是什么,对吧?
(感谢haskell-cafe mailing list寻求帮助。)
你在GHCI REPL上得到一个解析错误,因为在没有类型上下文的情况下,GHCi会将表达式计算为类型()
(空元组类型)。
例如,这不会出错:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> read "()"
()
这是使用GHCi时隐式有效的-XExtendedDefaultRules
选项的结果。有关GHCi具有这些扩展违约规则的原因的详细信息,请参阅Type defaulting in GHCi中的GHC User Guide。
要了解此选项如何影响评估,您可以在禁用选项的情况下执行相同的实验:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> :set -XNoExtendedDefaultRules
Prelude> read "2"
<interactive>:3:1:
No instance for (Read a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
...
现在我们收到No instance for ...
错误消息。这条消息告诉您GHC不知道要返回哪种类型。
答案 3 :(得分:0)
method: "POST"
函数的上下文(Num a, Read a) =>
足以让solveRPN
弄清楚。{1}}在ghci中试试这个:
read