Haskell: 读取和类型签名

6

read在Prelude中定义为

read :: (Read a) => String -> a

这可以被用作例如read "1" :: Int

现在一个函数

readOne :: (Read a) => [String] -> (a, [String])
readOne (x:xs) = (read x,xs)

使用readOne ["1","foo"]时,会产生错误(如预期):

在约束条件中出现了模糊的类型变量'a':
'Read a' 源于 'readOne' 的使用于 :1:0-18
可能的解决方法: 添加一个类型签名以修复这些类型变量

但是readOne ["1","foo"] :: Int不能正常工作,而

readOneInt :: [String] -> (Int, [String])
readOneInt = readOne

正常工作:

> readOneInt ["1", "foo"]
(1,["foo"])

那么,我如何在不定义新函数(如readOneInt)的情况下为readOne添加类型签名呢?
1个回答

9

readOne ["1","foo"] :: Int 无法工作,因为 readOne 不可能返回一个 Int,它总是返回一个元组,其第二个元素是一个 [String]readOne ["1", "foo"] :: (Int, [String]) 将会工作。

请注意,只有在类型无法推断时才需要指定类型。如果你将 readOne 的结果用于需要是一个 Int 的上下文中,你可以使用没有类型注释的 readOne。例如:

let inc (i, strs) = (i + 1, strs) in
inc (readOne ["1", "foo"])
-- (2, ["foo"])

2
注意!最后的示例只能在ghci中工作,因为1的类型默认为“Integer”。如果您通过:t询问表达式的类型,您将得到(Num a, Read a) => (a, [String]),这与之前的问题相同。asTypeOf可以帮助解决这个问题,例如asTypeOf (readOne ["1", "foo"]) (0::Double,[]::[String]) - 我通常会用反引号中的asTypeOf来编写,但SO不喜欢双重转义的反引号。 - yatima2975

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接