Haskell 为什么read无法解析只有一个参数的类型?
Haskell教程指出:Haskell 为什么read无法解析只有一个参数的类型?,haskell,typeclass,Haskell,Typeclass,Haskell教程指出: 通过查看read的类型签名 read::read a=>String->a 因此,在运行时,GHCI无法知道我们想要返回哪种类型 ghci>阅读“4” 为什么需要提供第二个值,GHCI可以从中提取要比较的类型 将一个值和所有可能的Readtypeclass类型进行比较难道不可行吗 参考: 将单个值与所有可能的ReadTypeClass类型进行检查难道不可行吗 这样做会产生同样的结果读取“4”可能是可以从字符串中读取的任何内容,这就是ghci报告的内容: Prelu
- 通过查看
read::read a=>String->aread的类型签名
- 因此,在运行时,GHCI无法知道我们想要返回哪种类型 ghci>阅读“4”
Read
typeclass类型进行比较难道不可行吗
参考:
将单个值与所有可能的ReadTypeClass类型进行检查难道不可行吗
这样做会产生同样的结果<代码>读取“4”可能是可以从字符串中读取的任何内容,这就是ghci
报告的内容:
Prelude> :t read "4"
read "4" :: Read a => a
在实际执行解析之前,reada=>a
表示潜在的解析结果。请记住,TypeClass是开放的,这意味着它可能是任何类型,具体取决于实例的存在
多个类型也完全可能共享相同的Show
/Read
文本表示,这就引出了我的下一点
如果您想检查字符串可以被解析为什么类型,那么至少需要解决可以接受给定输入的多个类型之间的歧义;这意味着您需要事先知道这些类型,Read
无法做到这一点。即使你这样做了,你又如何提出这样的价值观呢?你需要把它打包成一些东西,这意味着你需要一个封闭的集合
总而言之,read
签名在特定情况下尽可能精确。并非作为答案,但这并不完全适合于评论
在ghci中,如果您只需执行一个读取“5”
,那么ghci将需要一些帮助来确定您希望它是什么。但是,如果在某个地方使用了该结果,ghci(通常还有Haskell)可以找出类型。举个(愚蠢的)例子:
在这种情况下,不需要使用类型签名对读取进行注释,因为ghc可以从以下事实推断:five
正在一个只接受Int的函数中使用。如果您添加了另一个具有不同签名的函数,该函数也尝试使用five
,则最终会出现编译错误:
-- Adding this to our code above
-- Fails to compile
add1Integer :: Integer -> Integer
add1Integer i = i + 1
sixAsInteger = add1Integer five
我认为你(在初学者中很常见——我自己也有过)对课程类型的误解。Haskell的工作方式在逻辑上与“对照Read typeclass的所有可能类型检查单个值”不兼容。实例选择基于类型。只有类型
您不应该将read
看作是一个可以返回多种类型的神奇函数。它实际上是一个庞大的函数族,类型用于选择要使用的函数族成员。重要的是依赖的方向。类创建了一种情况,在这种情况下,值(通常是函数,但不总是)——运行时存在的东西——是根据类型选择的——编译时存在的东西
你会问“为什么不是另一个方向?为什么类型不能依赖于值?”,答案是Haskell不是这样工作的。它不是设计来的,它所基于的理论也不允许它。这有一个理论(依赖类型),GHC中添加了一些扩展,它们支持越来越多的功能集,这些功能在某些方面完成了依赖类型的工作,但目前还没有
即使是这样,这个例子仍然不能按照你想要的方式工作。依赖类型仍然需要知道某物是什么类型。你无法编写一个神奇的read
的“返回任何东西”版本。相反,read
的类型必须包含一些根据值计算类型的函数,并且本质上只适用于函数可以返回的封闭类型集
不过,最后两段有点像旁白。重要的一点是类是从类型到值的一种方式,通过方便的编译器支持,您可以在大多数情况下自动找到它。这就是他们设计要做的,也是他们能做的。这种设计的优点在于易于编译、行为的可预测性(开放世界假设)以及在编译时进行优化的能力。您建议阅读“5”
应该具有什么具体类型?说比那更糟不是那么简单。通常你不使用读“5”
,因为你只需要写5
。readsomeunknownstring
应该有什么具体类型?错误读取/解析与成功的结果相同。例如,如果我需要一个数字,而用户发送了其他内容,那么我希望接收一个解析错误,而不是其他内容。类型签名根本不相似show
的参数类型是多态的,这是很正常的。但是,read
的结果类型是多态的,这意味着如果没有类型签名的明确指示,或者没有使用结果的其他函数的推断,编译器就无法理解您想要什么。您会说,“为什么需要提供第二个值?”。这个问题使我困惑。没有必要——事实上,在大多数设置中,甚至不允许——提供第二个值。例如,read“4”3
将给您一个缺少实例的错误(在缺少某些非常非正统的附加代码的情况下)。你能举例说明你的意思吗,因为你在这里似乎没有使用标准术语?嗯,也许这很幼稚,但当字符串的值为“4”时,a
在读取
签名中表示的所有潜在类型都受到约束。我想要的
-- Adding this to our code above
-- Fails to compile
add1Integer :: Integer -> Integer
add1Integer i = i + 1
sixAsInteger = add1Integer five