Haskell如何阅读'推断类型

时间:2016-05-27 13:27:14

标签: haskell types type-inference

我之前在使用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):[]或者什么?

4 个答案:

答案 0 :(得分:2)

让我们倒退。

  1. head . foldl foldingFunction [] . words :: Num a => String -> a
  2. foldl foldingFunction [] .words :: Num a => String -> [a]
  3. foldl foldingFunction [] :: Num a => [String] -> [a]
  4. 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