了解readMaybe(Text.Read)

时间:2018-02-07 12:34:18

标签: haskell

我目前正在学习Haskell并对Joachim Breitner's online course CIS194中的这个示例有疑问:

import Text.Read
main = putStrLn "Hello World. Please enter a number:" >>
       getLine >>= \s ->
       case readMaybe s of   -- why not `readMaybe s :: Maybe Int` ?!
           Just n ->  let m = n + 1 in
                      putStrLn (show m)
           Nothing -> putStrLn "That’s not a number! Try again"

代码完全符合预期,即如果输入是整数则返回整数+1,并返回“那不是数字!再试一次”否则(例如如果输入是一个Double)。 我不明白,如果readMaybe s类型为Just nn仅返回IntreadMaybe的类型为readMaybe :: Read a => String -> Maybe a,因此我认为只有在读取该行时才会有效:

case readMaybe s :: Maybe Int of

事实上,如果我只是在ghci中提示> readMaybe "3",则会返回Nothing,而> readMaybe "3" :: Maybe Int会返回Just 3

总而言之,我的问题如下:编译器现在如何将s解析为Int而不是其他东西(例如 {{1} })没有使用Double?为什么不是每次都返回:: Maybe Int

我希望我的问题很清楚,非常感谢你的帮助。

2 个答案:

答案 0 :(得分:5)

TL; DR: Num a => Maybe a的上下文告诉我们它是Maybe Integer,默认为readMaybe

我们必须查看使用Nothing的结果来确定其类型的所有地方。

我们有

  • a,它没有告诉我们 aynthing 关于Just n
  • nm = n + 1用于上下文m = n + 1

n以来,我们现在知道Num的类型必须是(+) :: Num a => a -> a -> a的实例,因为1 :: Num a => atopdecl -> default (type1 , ... , typen) (n>=0) 。此时类型不明确,因此gets defaulted

  

4.3.4模糊类型和重载数值运算的默认值

let x = read "..." in show x -- invalid
     

Haskell样式重载所固有的问题是模糊类型的可能性。例如,使用第10章中定义的read和show函数,并假设只有Int和Bool是Read和Show的成员,那么表达式

show  :: forall a. Show a =>a ->String
read  :: forall a. Read a =>String ->a
     

含糊不清,因为显示和阅读的类型,

default
     

可以通过在两种情况下将一个实例化为Int或Bool来满足。这种表达式被认为是错误的,是一种静态错误。

     

我们说表达式e有一个模糊的类型,如果,在它的类型forall u。 cx => t,u中有一个类型变量u,它出现在cx中但不出现在t中。这些类型无效。

Haskell报告中定义的default (Integer, Double)Integer,例如GHC首先尝试Double,如果不起作用,则尝试使用Integer

由于m = n + 1是上下文m :: Integer中的有效类型,因此我们n :: IntegerreadMaybe s :: Maybe Integer,最后default

如果您要停用default (),请使用ST_Line_Substring,您会受到模糊类型错误的欢迎,就像您预期的那样。

答案 1 :(得分:1)

由于类型推断的工作原理,确实存在一些潜在的魔力。

这是一个更简单的例子,在GHCi中运行:

> print (1 :: Integer)
1
> print (1 :: Float)
1.0
Prelude> print 1
1

在最后一行中,1Num a => a类型的多态值,即IntegerFloat等任何数字类型内的值。如果我们在Integer类型中考虑该值,则将其打印为“1”。如果我们将其视为Float,我们将其打印为“1.0”。其他数字类型甚至可能有不同的打印格式。

但是,最后一行中的GHCi决定1Integer。为什么呢?

嗯,事实证明代码含糊不清:毕竟1可以用不同的方式打印!由于模糊性,Haskell在这种情况下会引发错误。但是,它使数字类型(那些包含Num)的例外更方便编程。具体地说,当代码没有精确确定数字类型时,Haskell使用其默认规则,它指定应该使用哪些数字类型。

GHC can warn当发生违约时,如果需要的话。

此外,传播类型。如果我们评估

case readMaybe s of
  Just x -> let z = x + length ['a','z']
            in ...

GHC知道length会返回Int。此外,(+)仅对相同类型的参数进行操作,因此x也必须是Int。这反过来意味着调用readMaybe s必须返回Maybe Int。因此,选择Read s的Int实例。

请注意类型推理引擎如何向后传播此信息,以便程序员不必添加可从其余代码推断出的类型注释。它在Haskell中经常发生。

一个可以是明确的,如

readMaybe s :: Maybe Int
-- or, with extensions on, one can mention the variable part of the type, only
readMaybe s @ Int

如果您愿意,可以随意添加此类注释。有时,它们会使代码更具可读性,因为它们记录了您的意图。无论谁读取代码,都可以在不查看上下文的情况下立即发现正在使用的Read实例。