Haskell中的强类型事件

时间:2014-02-25 20:58:48

标签: haskell event-sourcing strong-typing

我正在研究我的第一个“真正的”Haskell项目,同时试图让我的头脑event sourcing。 (这似乎是一个很好的匹配;事件采购是一种相当实用的数据查看方式。)

我试图弄清楚如何将我的事件反序列化为强类型的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

  3. 最后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更具表现力的问题。

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

1 个答案:

答案 0 :(得分:3)

如果您让deserialize成为Event课程的成员,那么您将不会遇到任何问题:

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

instance Event PlayerEvent where
    ...

instance Event GameEvent where
    ...