1

我正在阅读“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”函数之间进行选择?

4

2 回答 2

6

如果您将fromJValue someValue其用作某些表达式的一部分,Haskell 会选择表达式所需的类型。因此,例如,如果您这样做:

case fromJValue someValue of 
    Right str -> putStr str
    Left _ -> error

选择的实例JSON String是因为putStr需要一个字符串。

在无法明确确定所需类型的情况下,您将收到“模糊类型变量”错误,并且必须添加类型注释以手动选择要使用的实例。

请注意,与哪种JValue someValue包含完全无关。

于 2012-09-17T01:04:14.807 回答
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

现在 if xis "{ foo : 222 }",实例仍然是 for String,所以Left会调用 branch 并且解析器会说“不是 JSON 字符串”。

另一个小点 - 在生产中你应该使用returnand throwErrorfromMonadError而不是LeftandRight让你的意图更清晰。但我认为这只是一个教程代码。

于 2012-09-17T04:56:32.843 回答