我正在阅读“Real World Haskell”(好书),我对编译器如何选择重载函数感到困惑。
如果我有类型
type JSONError = String
class JSON a where
toJValue :: a -> JValue
fromJValue :: JValue -> Either JSONError a
和两个像这样的实例
instance JSON Bool where
toJValue = JBool
fromJValue (JBool b) = Right b
fromJValue _ = Left "not a JSON boolean"
和
instance JSON String where
toJValue = JString
fromJValue (JString s) = Right s
fromJValue _ = Left "not a JSON string"
编译器如何在两个“fromJValue”函数之间进行选择,例如,给出一个Integer?
答案 0 :(得分:6)
如果使用fromJValue someValue
作为某个表达式的一部分,Haskell将选择表达式所需的类型。所以,如果您这样做:
case fromJValue someValue of
Right str -> putStr str
Left _ -> error
选择了JSON String
的实例,因为putStr
需要一个字符串。
如果无法明确确定所需类型,则会出现“模糊类型变量”错误,并且必须添加类型注释以手动选择要使用的实例。
请注意,JValue
someValue
包含哪种内容完全不相关。
答案 1 :(得分:1)
在OO语言中,如果返回一个对象,则虚拟方法表与一起返回。在Haskell中,如果返回类型类的成员,则字典作为隐藏参数传入。
所以在运行时没有问题 - 隐藏的字典参数就像VTable一样用于实现。
现在你的问题就变成了编译器最初选择实例的方式。
了解fromJValue
的使用方式。
首先是包含JSON数据的字符串,然后将其传递给解析函数:
foo :: String
foo = parseJsonString x
现在编译器知道它必须在调用链上传递instance JSON String
的字典。
parseJsonString
(我不记得正确的名称)将您的字符串解析为JSON数据类型。像
data ParsedJSON = JBool Bool | JString string | JObject ...
parseJsonString :: JSON a => String -> Either JSONError a
首先,它尝试将传递的字符串转换为ParsedJSON
数据结构。
其次,它将已经拥有的结构和字典传递给fromJValue
。
现在如果x
为"{ foo : 222 }"
,则实例仍为String
,因此将调用Left
分支并且解析器会说"而不是JSON字符串"
另一个小问题 - 在制作中,您应该使用return
而throwError
代替MonadError
和Left
,以使您的意图更加清晰。但我认为它只是一个教程代码。