Haskell中的强类型事件

Ben*_*son 6 haskell strong-typing event-sourcing

我正在研究我的第一个"真正的"Haskell项目,并同时试图了解事件采购.(这似乎是一个很好的匹配;事件采购是一种相当实用的数据查看方式.)

我试图弄清楚如何将我的事件反序列化为强类型的Haskell数据.这里有两种相反的力量在起作用:

  1. 不应该将事件应用于错误类型的聚合.这个要求表明我的系统中每个聚合都需要一个单独的事件类型:

    data PlayerEvent = PlayerCreated Name | NameUpdated Name

    data GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move

    要使用这些事件,您可以使用类型为的函数applyEvent :: Game -> GameEvent -> Game.

  2. 我需要能够在强类型事件和JSON对象之间进行序列化和反序列化.这个要求表明我需要多态serialisedeserialise功能:

    class Event e where serialise :: e -> ByteString

    deserialise :: Event e => ByteString -> e

最后一个deserialise功能就是问题.类型签名表示调用者可以请求任何实例Event,但当然,您获取的类型取决于所引入的类型ByteString并在运行时确定.

这是一个无法编译的存根实现:

deserialise :: Event e => ByteString -> e
deserialise _ = GameStarted 0 0
Run Code Online (Sandbox Code Playgroud)

并且错误消息:

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
Run Code Online (Sandbox Code Playgroud)

在具有反射的面向对象语言中,这种事情是直截了当的.我发现很难相信我发现Java的类型系统比Haskell更具表现力的问题.

我觉得我必须在这里错过一个关键的抽象.实现上述要求的正确方法是什么?

bhe*_*ilr 4

如果你成为班级deserialize的一员Event,那么你就不会有任何问题:

class Event e where
    serialize :: e -> ByteString
    deserialize :: ByteString -> e

instance Event PlayerEvent where
    ...

instance Event GameEvent where
    ...
Run Code Online (Sandbox Code Playgroud)