【发布时间】:2014-03-28 09:00:02
【问题描述】:
我正在处理我的第一个“真正的”Haskell 项目,同时试图了解event sourcing。 (这似乎是一个很好的匹配;事件溯源是一种查看数据的相当实用的方式。)
我在试图弄清楚如何将我的事件反序列化为强类型 Haskell 数据时遇到了困难。这里有两种相反的力量在起作用:
-
不应将事件应用于错误类型的聚合。此要求表明我需要为系统中的每个聚合使用单独类型的事件:
data PlayerEvent = PlayerCreated Name | NameUpdated Namedata GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move要使用这些事件,您可以使用类型为
applyEvent :: Game -> GameEvent -> Game的函数。 -
我需要能够在强类型事件和 JSON 对象之间进行序列化和反序列化。这个要求表明我需要多态
serialise和deserialise函数:class Event e where serialise :: e -> ByteStringdeserialise :: Event e => ByteString -> e
最后一个deserialise 函数是问题所在。类型签名表明调用者可以请求Event 的任何 实例,但当然,您返回的类型取决于传入的ByteString,并在运行时确定。
这是一个无法编译的存根实现:
deserialise :: Event e => ByteString -> e
deserialise _ = GameStarted 0 0
还有错误信息:
Could not deduce (e ~ GameEvent)
from the context (Event e)
bound by the type signature for
deserialise :: Event e => ByteString -> e
at ...:20:16-41
`e' is a rigid type variable bound by
the type signature for deserialise :: Event e => ByteString -> e
at ...:20:16
In the return type of a call of `GameStarted'
In the expression: GameStarted 0 0
In an equation for `deserialise':
deserialise _ = GameStarted 0 0
这种事情在带有反射的面向对象语言中很简单。我很难相信我发现了一个问题,Java 的类型系统比 Haskell 的类型系统更具表现力。
我觉得我必须在这里遗漏一个关键的抽象。实现上述要求的正确方法是什么?
【问题讨论】:
-
为什么不让
deserialize成为Event的成员?问题是您说deserialize可以返回anyEvent,但您具体说它返回GameEvent。如果它是Event类的一部分,那么您将获得所需的多态性。 -
另外,并不是 Java 的类型系统更具表现力,而是你试图让 Haskell 的类型系统表现得像 Java 的那样,但那是行不通的。
-
@bheklilr 非常感谢。我不知道为什么我一开始没有想到让
deserialise成为Event的成员。而且,我同意尝试用 Haskell 编写 Java 是一个错误。但是,要从根本上重新连接一个人的大脑仍然很困难! -
PS 如果你想把它放在一个完整的答案中,我会接受它,十五分将是你的 :)
-
值得看看这个静态类型的 ES 的 Haskell 实现:gist.github.com/Fristi/7327904,原来的主题是slideshare.net/mobile/chris.e.richardson/…
标签: haskell event-sourcing strong-typing