Haskell中的强类型事件 [英] Strongly typed events in Haskell

查看:162
本文介绍了Haskell中的强类型事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我试图弄清楚如何反序列化我的墙事件转换为强类型的Haskell数据。这里有两种对立的力量:


  1. 应该不可能将事件应用于错误类型的聚合。这个要求表明,我需要为我的系统中的每个聚合分别使用一个事件类型:

    data PlayerEvent = PlayerCreated Name | NameUpdated Name



    data GameEvent = GameStarted PlayerID PlayerID | MoveMade PlayerID Move



    要使用这些事件,您可以使用类型为 applyEvent :: Game - > GameEvent - >游戏


  2. 我需要能够在强类型事件和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:

  1. 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.

  2. I need to be able to serialise and deserialise between strongly-typed events and JSON objects. This requirement suggests I need polymorphic serialise and deserialise 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆