3

この質問はgroundhogまたはに関するものですpersistent。どちらも同じ問題を共有していると私は信じているからです。

Tr m aいくつかの機能を提供するトランスがあるとしますf :: Int -> Tr m ()。この機能には、データベースへのアクセスが必要です。ここで使用できるオプションはいくつかありますが、満足できるものはありません。

DbPersistのどこかに変圧器を置くことができTrます。実際には、標準のトランスフォーマーのインスタンスがなく、さらにnewtypePersistBackendのインスタンスを作成する必要があるため、これを一番上に配置する必要があります。Trクラスが最小からかけ離れているため、これはすでにひどいものです。私が行うすべての db アクションを持ち上げることもできます。

もう 1 つのオプションは、 の署名を に変更するfことPersistBackend m => Int -> Tr m ()です。これもまた、私のnewtype でPersistBackendインスタンスを作成するか、リフティングする必要があります。Tr

ここからが本当の問題です。すでに制約Trがあるコンテキスト内で実行するにはどうすればよいですか? PersistBackendと共有する方法はありませんTr

最初のオプションを実行して、いくつかの新しい接続プールDbPersist内で実際のトランスフォーマーを実行するTrことができます (私が既にいるコンテキストからプールを取得する方法がないことがわかる限りPersistBackend)、または 2 番目のオプションを実行することができます。実行関数を にしますrunTr :: PersistBackend m => Tr m a -> m a。2番目のオプションは実際には完全に問題ありませんが、ここでの問題は、DbPersist最終的にスタックのどこかになければならない が、現在トランスフォーマーの下にあり、構成されている標準トランスフォーマーのインスタンスTrがないことです。PersistBackendTr

ここで正しいアプローチは何ですか?現時点では、ReaderTリクエストに応じて接続プールを提供するスタックのどこかにセパタレrunDbConnを使用し、DB にアクセスしたいすべての場所でそのプールを使用するのが最善の選択肢のようです。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

彼らは for のインスタンスを書きましたReaderT conn m(は廃止され、にDbPersistエイリアスされましたReaderT conn) 。の代わりにインスタンス化する必要があるため、これはモナド トランスフォーマーではありませんが、これと関連するデータ型のトリックを使用すると、大騒ぎせずにカスタム モナド スタックを使用できるはずです。Tr (ReaderT conn)ReaderTmtlTr mTr

どちらのオプションを選択しても、おそらく持ち上げる必要があります。私の個人的な意見ReaderT connでは、スタックの一番外側に固執します。そうすれば、mtlヘルパーはスタックのほとんどを持ち上げることができ、追加のリフトを接着して家に持ち帰ることができます。そして、Hackage のバージョンに固執する場合、これが唯一の合理的なオプションのようです。そうしないと、(古い) モノリシックPersistBackendクラスになるからです。

持続的に

ReaderT SqlBackendPersistent はもう少し単純です: モナド変換子スタックが を含み、 で終了する限り、IOへの呼び出しを持ち上げることができますrunSqlPool :: MonadBaseControlIO m => ReaderT SqlBackend m a -> Pool SqlBackend -> m a。すべての Persistent 操作は type の何かを返すように定義されているReaderT backend m aため、設計はうまくいきます。

于 2016-05-03T20:26:38.890 に答える