Haskell中的强类型事件 [英] Strongly typed events in Haskell
问题描述
我正在研究我的第一个'真正的'Haskell项目,同时尝试让我的头脑在事件采购。 (这似乎是一个很好的匹配;事件采购是查看数据的一种非常实用的方式。)
我试图弄清楚如何反序列化我的墙事件转换为强类型的Haskell数据。这里有两种对立的力量:
-
应该不可能将事件应用于错误类型的聚合。这个要求表明,我需要为我的系统中的每个聚合分别使用一个事件类型:
data PlayerEvent = PlayerCreated Name | NameUpdated Name
data GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move
要使用这些事件,您可以使用类型为
applyEvent :: Game - > GameEvent - >游戏
。 -
我需要能够在强类型事件和JSON对象之间进行序列化和反序列化。这个需求表明我需要多态
serialise
和deserialise
函数:
class Event e where serialise :: e - > ByteString
deserialise :: Event e => ByteString - > e
上一个 deserialise
功能是问题。类型签名表示调用者可以请求事件
的任何实例,但是当然返回的类型取决于 ByteString
,它是在运行时确定的。
下面是一个不能编译的存根实现:
deserialise :: Event e => ByteString - > e
deserialise _ = GameStarted 0 0
和错误信息:
无法推断(e〜GameEvent)
来自上下文(Event e)
由类型签名绑定
deserialise :: Event e => ByteString - > e
at ...:20:16-41
`e'是一个刚性类型变量,由
绑定,用于deserialise :: Event e =>的类型签名。 ByteString - > e
at ...:20:16
在`GameStarted'调用的返回类型中
在表达式中:GameStarted 0 0
在`deserialise' :
deserialise _ = GameStarted 0 0
这种东西在对象 - 以反思为导向的语言。我发现很难相信我发现Java的类型系统比Haskell更具表现力。
我觉得我必须在这里错过一个关键的抽象。什么是实现上述要求的正确方法?
如果您制作反序列化
Event
类的成员,那么您将不会遇到任何问题:
class Event e where
serialize :: e - > ByteString
deserialize :: ByteString - > e
实例Event PlayerEvent其中
...
实例Event GameEvent其中
...
I'm working on my first 'real' Haskell project, and simultaneously trying to get my head around event sourcing. (It seemed like a good match; event sourcing is a rather functional way of looking at data.)
I've hit a wall trying to figure out how to deserialise my events into strongly typed Haskell data. There are two opposing forces at work here:
It shouldn't be possible to apply an event to the wrong type of aggregate. This requirement suggests that I need a separate type of event for each aggregate in my system:
data PlayerEvent = PlayerCreated Name | NameUpdated Name
data GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move
To use these events you'd use functions with types like
applyEvent :: Game -> GameEvent -> Game
.I need to be able to serialise and deserialise between strongly-typed events and JSON objects. This requirement suggests I need polymorphic
serialise
anddeserialise
functions:class Event e where serialise :: e -> ByteString
deserialise :: Event e => ByteString -> e
That last deserialise
function is the problem. The type signature says that callers can ask for any instance of Event
, but of course the type that you get back depends on the ByteString
that came in and is determined at run-time.
Here's a stub implementation that won't compile:
deserialise :: Event e => ByteString -> e
deserialise _ = GameStarted 0 0
And the error message:
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
This sort of thing is straightforward in an object-oriented language with reflection. I find it hard to believe that I've found a problem for which Java's type system is more expressive than Haskell's.
I feel like I must be missing a key abstraction here. What's the correct way to implement the above requirements?
If you make deserialize
a member of the Event
class, then you won't have any problems:
class Event e where
serialize :: e -> ByteString
deserialize :: ByteString -> e
instance Event PlayerEvent where
...
instance Event GameEvent where
...
这篇关于Haskell中的强类型事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!