在我的应用程序中,我存储并加载数据库中的对象(当前是本地平面文件......)。这些对象都来自类型系列Event a
,也可以是ByteString
的可序列化对象。但类型系列中的a
可能会有所不同......
这是基类声明:
class BusinessModel a where
data Event a :: *
apply :: a -> Event a -> a
一个示例实现,以及所需的类型:
data User = User { userId :: UserId, userName :: Text } deriving (Eq, Show, Read, Generic)
instance ToJSON User
instance FromJSON User
type UsersView = M.Map UserId User
instance BusinessModel UsersView where
data Event UsersView = NoEvent -- ^A no-op event
| UserRegistered User
deriving (Eq, Show, Read, Generic)
apply v (UserRegistered u) = insert (userId u) u v
apply v _ = v
以下是我的活动商店的当前界面:
class (MonadIO store) => EventStore store where
-- store a single event
store :: Serializable s => s -> store ()
-- load all events from the store
load :: Serializable s => store [s]
然后我们需要能够序列化事件(这里我们只使用Json表示:
instance ToJSON (Event UsersView)
instance FromJSON (Event UsersView)
instance Serializable (Event UsersView) where
read = fromJust . decode
write = encode
我希望能够为所有Event a
反序列化所有存储的a
个对象,然后将每个Event a
应用于包含不同内容的结构中的正确a
事件"目标"。当我试图天真地使用load
时,我遇到了Serializable
多个实例并且无法选择正确实例的麻烦。
在我的脑海中,我可以想到一个简单的解决方案:用适当的a
标记每个要阅读的事件,但这看起来不是很优雅?
解决此类问题的最佳方法是什么?
答案 0 :(得分:2)
在Haskell中确实没有方便的自动方法来实现您的目标。反序列化任意值并通过接口以运行时多态方式使用它们更像是面向对象的模式。
正如您所说,对于序列化和反序列化,您需要以某种方式标记事件以存储和恢复类型信息。 E.g。
data TaggedEvent
= EventUsersView (Event UsersView)
| EventSomeOtherView (Event SomeOtherView)
...
要删除一些手动样板文件,可以使用模板Haskell自动生成TaggedEvent
类型,例如acid-state
中的事件。