3

这个问题是关于groundhogor persistent,因为我相信两者都有相同的问题。

假设我有一个Tr m a提供一些功能的变压器f :: Int -> Tr m ()。此功能需要数据库访问。我可以在这里使用一些选项,但没有一个是令人满意的。

我可以DbPersist在里面的某个地方放一个变压器Tr。实际上,我需要将它放在顶部,因为没有PersistBackend标准转换器的实例,而且我仍然需要为我的新类型编写一个实例Tr。这已经很糟糕了,因为课程远非最小。我也可以解除我所做的每一个 db 操作。

另一种选择是更改fto的签名PersistBackend m => Int -> Tr m ()。这将再次需要PersistBackend我的Trnewtype 上的实例或提升。

现在这是真正的问题。如何Tr在已经有PersistBackend约束的上下文中运行?没有办法与它分享Tr

我可以执行第一个选项并使用一些新的连接池DbPersist在其中运行实际的转换Tr器(据我所知,无法从PersistBackend我已经在其中的上下文中获取池),或者我可以执行第二个选项并具有运行功能runTr :: PersistBackend m => Tr m a -> m a。第二个选项实际上完全没问题,但这里的问题是,DbPersist最终必须在堆栈中的某个位置,现在位于Tr变压器下方,并且没有PersistBackend标准变压器的实例Tr

这里的正确方法是什么?目前看来,最好的选择是ReaderT在堆栈中的某个地方使用一个分隔符,根据请求为我提供连接池,然后runDbConn在我想访问数据库的任何地方使用该池。看到DbPersist基本上已经只是一个ReaderT我不明白必须这样做的意义。

4

1 回答 1

3

土拨鼠

我建议使用他们分支中最新的土拨鼠。master尽管我将要描述的更改似乎已在 2015 年 9 月实施,但还没有发布到 Hackage。但作者似乎已经解决了这个问题。

提示,PersistBackend现在是一个更简单的类实现,大大减少了它曾经是数十种方法的庞然大物:

class (Monad m, Applicative m, Functor m, MonadIO m, ConnectionManager (Conn m), PersistBackendConn (Conn m)) => PersistBackend m where
  type Conn m
  getConnection :: m (Conn m)

instance (Monad m, Applicative m, Functor m, MonadIO m, PersistBackendConn conn) => PersistBackend (ReaderT conn m) where
  type Conn (ReaderT conn m) = conn
  getConnection = ask

他们为ReaderT conn m(DbPersist已弃用并别名为) 编写了一个实例,如果您选择放入内部而不是外部的路线ReaderT conn,您可以轻松地编写一个实例。它不是一个monad 转换器,因为您必须实例化而不是,但是这个和他们使用的相关数据类型技巧应该允许您使用自定义 monad 堆栈而不会大惊小怪。Tr (ReaderT conn)ReaderTmtlTr mTr

您选择的任何一个选项都可能需要一些提升。在我个人看来,我会坚持ReaderT conn在堆栈的最外面。这样,mtl助手仍然可以通过您的大部分堆栈抬起,您可以粘上额外的升降机将其带回家。而且,如果您坚持使用 Hackage 上的版本,这似乎是唯一合理的选择,因为否则您将拥有(旧的)整体PersistBackend类。

执着的

持久性更简单一点:只要 monad 转换器堆栈包含ReaderT SqlBackend并终止于IO,您就可以取消对runSqlPool :: MonadBaseControlIO m => ReaderT SqlBackend m a -> Pool SqlBackend -> m a. 所有 Persistent 操作都被定义为返回 type 的东西ReaderT backend m a,所以设计很简单。

于 2016-05-03T20:26:38.890 回答