haskell - 如何在 monad 转换器堆栈中对可变键/值映射进行建模?

标签 haskell monad-transformers

这是一个设计问题。

我目前有一个“App”类,它是一个 monad 转换器堆栈,我想添加到该堆栈的是一个可变的键/值存储。

我希望能够轻松更改该商店的具体实现方式。最初我希望它只是内存中的Map ,但稍后我可能会将其更改为基于数据库的东西。

首先想到的就是添加 StateT到我的 monad 变压器堆栈。这对于内存中的 Map 来说效果很好。 ,但我认为这不适用于数据库实现,因为我不想每次都获取/更新整个状态,而只想获取/更新一个键/值组合。

第二个想法是编写我自己的 monad 转换器。但是当我查看现有的变压器时,它们都具有带有所有其他 monad 变压器实例的类,因此它们可以与其他变压器一起工作。我认为添加我自己的 monad 变压器并允许它与所有其他变压器良好地交互将涉及编写 n (或者实际上可能是 2 * n 实例),因此它可以穿透其他变压器,而其他变压器也可以穿透它。从技术上讲,我只需要使用我正在使用的变压器来完成此操作,但这似乎有点老套。关于 monad 转换器堆栈是否是一个好主意,这里可能还有另一个争论,但我不想在此时完全改变我的应用程序架构。

所以我的第三个想法是拥有一个 ReaderT带有我可以替换的函数记录(或者可能是带有像 data Blah where Blah :: c => Blah 这样的类型类约束的记录)。我认为我可以开始工作,但我不确定这是最好的方法,我必须分层 ReaderTStateT 之上我认为是内存中的方法。

作为我猜的第四种方法,我确实写出了一堆类型类,如下所示:

data HadKey = KeyFound | KeyNotFound

class Monad m => LookupMapM key value m where
  lookupM :: key -> m (Maybe value)

class LookupMapM key value m => InsertMapM key value m where
  insertM :: key -> value -> m HadKey

class InsertMapM key value m => UpdateMapM key value m where
  updateM :: key -> value -> m HadKey

然后我开始实现接口(interface),如下所示:

instance LookupMapM key value (StateT (Map key value) m) where ...

instance (...) => LookupMapM key value IO where ...

这看起来很有希望,但第二个实例似乎有问题......就像,我只能有一个 IO实现(想必不同的数据库应该能够有自己的实现)。

我想我可以使用包 tagged identity 解决上述问题,这样我就可以标记不同的实现。

我注意到,如果我的“ map ”变压器不在堆栈的顶部,我仍然需要抬起它,但这不是一个大问题,我可以只维护 liftKV只需要一个额外的函数 lift每次将一层添加到 monad 转换器堆栈时,我认为这没什么大不了的。

但也许有比我建议的方法更好的方法?或者也许我建议的方法之一是最好的方法,我只是不确定哪一种。

通过其他想法,这里不是:

class Monad m => LookupMapM key value m where
  lookupM :: key -> m (Maybe value)

采用这种风格:

class Monad m => LookupMapM t m where
  type Key t
  type Value t
  lookupM :: Key t -> m (Maybe (Value t))

但我不确定这是否能解决任何问题,或者只是增加额外的复杂性而没有任何好处。

有什么想法吗?

最佳答案

您必须将IO放在堆栈的底部。除此之外,您将需要一个带有数据库连接信息和通过它访问数据库的函数的ReaderT。然后将您需要的其他内容放在上面。

有关此类事情的示例,请参阅 Render开罗图书馆的 monad。它存储渲染上下文而不是数据库连接,但概念是相同的。

关于haskell - 如何在 monad 转换器堆栈中对可变键/值映射进行建模?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76125393/

相关文章:

haskell - 使函数成为向量类型类的实例

haskell - 在 monad 转换器中,为什么已知的 monad 是内部的?

haskell - 将 Maybe 包装在 WriterT 中以添加日志记录

Haskell:HList 和可选参数

haskell - Haskell 中的引用透明度困惑

haskell - 编写指称语义映射函数需要什么?

haskell - Haskell 如何在 MaybeT 实现中推断出正确的类型类?

haskell - GHC 可以为 monad 转换器派生 Functor 和 Applicative 实例吗?

scala - 在scalaz中堆叠StateT

haskell - 如何使用 GADT 将字符串解析为语法树