我正在研究我的第一个“真正的”Haskell项目,同时试图让我的头脑event sourcing。 (这似乎是一个很好的匹配;事件采购是一种相当实用的数据查看方式。)
我试图弄清楚如何将我的事件反序列化为强类型的Haskell数据。这里有两种相反的力量在起作用:
不应该将事件应用于错误类型的聚合。这个要求表明我的系统中每个聚合都需要一个单独的事件类型:
data PlayerEvent = PlayerCreated Name | NameUpdated Name
data GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move
要使用这些事件,您可以使用类型为applyEvent :: Game -> GameEvent -> Game
的函数。
我需要能够在强类型事件和JSON对象之间进行序列化和反序列化。此要求表明我需要多态serialise
和deserialise
函数:
class Event e where serialise :: e -> ByteString
deserialise :: Event e => ByteString -> e
最后deserialise
函数是问题所在。类型签名表示调用者可以请求 Event
的任何实例,但当然您获取的类型取决于进入的ByteString
并在运行时确定-time。
这是一个无法编译的存根实现:
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更具表现力的问题。
我觉得我必须在这里错过一个关键的抽象。实施上述要求的正确方法是什么?
答案 0 :(得分:3)
如果您让deserialize
成为Event
课程的成员,那么您将不会遇到任何问题:
class Event e where
serialize :: e -> ByteString
deserialize :: ByteString -> e
instance Event PlayerEvent where
...
instance Event GameEvent where
...