处理变换器堆栈中的数据库访问

时间:2016-05-03 14:49:58

标签: database haskell monad-transformers haskell-persistent

这个问题与groundhogpersistent有关,因为我认为两者都有同样的问题。

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

我可以在DbPersist内的某处放置Tr变换器。实际上,我需要将它放在顶部,因为标准变换器没有PersistBackend个实例,我仍然需要为我的Tr newtype编写一个实例。这已经很糟糕,因为这个课程远没有那么简单。我也可以解除我做的所有数据库操作。

另一个选项是将f的签名更改为PersistBackend m => Int -> Tr m ()。这将再次要求PersistBackend新类型上的Tr实例或提升。

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

我可以执行第一个选项并使用一些新的连接池在DbPersist内运行实际的Tr转换器(据我所知,没有办法从{{{{{{{ 1}}上下文我已经在,或者我可以做第二个选项并使run函数为PersistBackend。第二个选项实际上是完全正常的,但问题是最终必须在堆栈中的某个地方的runTr :: PersistBackend m => Tr m a -> m a现在位于DbPersist变换器下并且没有{{1标准变换器的实例,其中Tr由。{/ p>组成

这里的正确方法是什么?目前似乎最好的选择是在堆栈中的某个地方使用sepatare PersistBackend,根据请求为我提供连接池,然后在我想要访问的任何地方对{池}进行Tr DB。看看ReaderT基本上已经只是runDbConn我不明白必须这样做。

1 个答案:

答案 0 :(得分:3)

土拨鼠

我建议使用master分行的latest groundhog。即使我将要描述的更改似乎已于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),如果你选择去,你可以轻松地为Tr (ReaderT conn)写一个把ReaderT放在里面而不是外面的路线。它不是一个mtl monad变换器,因为你必须实例Tr m而不是Tr,但是这和他们使用的相关数据类型技巧应该允许你使用自定义monad堆叠没有太大的麻烦。

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

持久

持久性稍微简单一点:只要monad变换器堆栈包含ReaderT SqlBackend并终止于IO,您就可以调用runSqlPool :: MonadBaseControlIO m => ReaderT SqlBackend m a -> Pool SqlBackend -> m a。所有持久化操作都被定义为返回ReaderT backend m a类型的东西,因此设计类型就可以解决了。