我目前正在学习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 n
,n
仅返回Int
。 readMaybe
的类型为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
?
我希望我的问题很清楚,非常感谢你的帮助。
答案 0 :(得分:5)
TL; DR: Num a => Maybe a
的上下文告诉我们它是Maybe Integer
,默认为readMaybe
。
我们必须查看使用Nothing
的结果来确定其类型的所有地方。
我们有
a
,它没有告诉我们 aynthing 关于Just n
n
,m = n + 1
用于上下文m = n + 1
。自n
以来,我们现在知道Num
的类型必须是(+) :: Num a => a -> a -> a
的实例,因为1 :: Num a => a
和topdecl -> default (type1 , ... , typen) (n>=0)
。此时类型不明确,因此gets default
ed:
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 :: Integer
,readMaybe s :: Maybe Integer
,最后default
。
如果您要停用default ()
,请使用ST_Line_Substring
,您会受到模糊类型错误的欢迎,就像您预期的那样。
答案 1 :(得分:1)
由于类型推断的工作原理,确实存在一些潜在的魔力。
这是一个更简单的例子,在GHCi中运行:
> print (1 :: Integer)
1
> print (1 :: Float)
1.0
Prelude> print 1
1
在最后一行中,1
是Num a => a
类型的多态值,即Integer
和Float
等任何数字类型内的值。如果我们在Integer
类型中考虑该值,则将其打印为“1”。如果我们将其视为Float
,我们将其打印为“1.0”。其他数字类型甚至可能有不同的打印格式。
但是,最后一行中的GHCi决定1
是Integer
。为什么呢?
嗯,事实证明代码含糊不清:毕竟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
实例。