非常に多くのハードコードされたインスタンスを必要としません....そして私は、私は不必要な何かをやったような気がします。、それは本当に私は長い時間モナド変換子のユーザー、初めてモナド変換子のライターをしています
私たちは複数のDBテーブルを持つプロジェクトに取り組んでおり、異なるモナド・スタックにセットをハードコーディングするのは扱いにくくなっていたので、プラグイン可能なモナド・トランスフォーマーに分割して、レベルは、このように
doSomething::(HasUserTable m, HasProductTable m)=>Int->m String
(HasXTableはコンクリートモナド変圧器です)。これらの独立したモナド・トランスは完全にモジュラー形式で挿入または削除でき、DBハンドルを格納し、ResourceTなどを必要とします。
私の最初の試みは、保持するのに使用されるReaderTをラップすることでしたDBハンドル。 ReaderT(およびStateTなど)は、ハードコードされた "リフト"のチェーンを使用せずに積み重ねることができず、スタック要素のプラガブルなモジュール性が破られるため、これがうまくいかないことがすぐに分かりました。
唯一の解決策は、ReaderTモナドのコピーを完全に別々に書くことであり、それぞれがより低いレベルの他のものへのアクセスを可能にするということでした。これは動作しますが、解決策は、定型的なコードで満たされ、これはさらに悪くなりますこれは何
class HasUserTable m where
getUser::String->m User
newtype UserTableT m r = UserTableT{runUserTableT::String->m r}
--Standard monad instance stuff, biolerplate copy of ReaderT
instance Functor m=>Functor (UserTableT m) where....
instance Applicative m=>Applicative (UserTableT m) where....
instance Monad m=>Monad (UserTableT m) where....
instance Monad m=>HasUserTable (UserTableT m) where....
--Gotta hardcode passthrough rules to every other monad transformer
--in the world, mostly using "lift"....
instance MonadTrans BlockCacheT where....
instance (HasUserTable m, Monad m)=>HasUserTable (StateT a m)....
instance (HasUserTable m, Monad m)=>HasUserTable (ResourceT m)....
.... etc for all other monad transformers
--Similarly, need to hardcode passthrough rules for all other monads
--through the newly created one
instance MonadResource m=>MonadResource (UserTableT m) where....
instance MonadState a m=>MonadState a (UserTableT m) where....
instance (MonadBaseControl IO m) => MonadBaseControl IO (UserTableT m)....
.... etc for all other monad transformers
のようなものは、我々が追加それぞれの新しいモナド変換(IE-それぞれの新しいテーブルのためにさらに多くのパススルールールを追加する必要があるということです私たちは、他のすべてのテーブルのモナド変圧器をパススルーする必要性を追加し、私たちは、n^2インスタンス宣言を必要とする!)
これを行うにはクリーンな方法はありますか?
これは、拡張可能な影響を非常に思い起こさせるように見えます。 https://hackage.haskell.org/package/free-vlには実装と、それを説明する論文への参照があります。 –
"n^2インスタンス宣言が必要なので"これはモンタージュトランスフォームのmtlスタイルに関するよく知られた問題です。あなたの型を 'ReaderT String m r'と書く代わりに、一般化されたnewtypeの導出を使って、読者のものと同じインスタンスを導き出すことができます(ここではほとんどの場合そうです)。ほとんどのインスタンスを 'MonadTrans t、HasUserTable m => HasUserTable(t m)'で置き換えることはできますが、この種の型推論は強制終了し、いくつかの拡張が必要です。 – user2407038
@ user2407038一般化された 'MonadTrans t、HasUserTable m => HasUserTable(t m)'を使用する際の問題は、UserTableTにも適用され、書く必要がある適切なインスタンスと競合していました。私はこれがなぜn^2問題が存在するのではないかと考えている(さもなければ、彼らはすべてのモナド変圧器に対してこれを行っただろう)。私はn^2問題に対するあなたのコメントは、私の質問に対する答えかもしれないと思います。しかし、幸せではありません....あなたはモナド変圧器、そしてたぶんハスケルでも何もできません。私はこの問題について議論している参考書を持っています。私はそれを答えとして受け入れます。 – jamshidh