はじめに、この長い投稿で申し訳ありません。
Haskell でイベント駆動型アプリケーションを作成しているため、さらに使用するためにいくつかのコールバック関数を保存する必要があります。そのようなコールバックを次のようにしたいと思います。
- 豊かな:裸の sではなく
ReaderT
、ErrorT
,の使用。StateT
IO
- ポリモーフィック: タイプ
(MonadIO m, MonadReader MyContext m, MonadState MyState m, MonadError MyError m) => m ()
ではなくReaderT MyContext (StateT MyState (ErrorT MyError IO)))
簡単にするために、層State
と層については忘れましょう。Error
内に保存されたすべてのコールバックの記録を書き始めましたMyContext
。次のようなものです。
data MyContext = MyContext { _callbacks :: Callbacks {- etc -} }
-- In this example, 2 callbacks only
data Callbacks = Callbacks {
_callback1 :: IORef (m ()),
_callback2 :: IORef (m ())}
主な問題は、型クラスの制約をどこに置くm
かです。次のことを試しましたが、コンパイルされたものはありません:
Callbacks
次のようにパラメータ化できると思いm
ました:data (MonadIO m, MonadReader (MyContext m) m) => Callbacks m = Callbacks { _callback1 :: IORef (m ()), _callback2 :: IORef (m ())}
Callbacks
の一部であるためMyContext
、後者もパラメータ化する必要があり、無限型の問題が発生します (MonadReader (MyContext m) m
)。次に、存在量指定子を使用することを考えました。
data Callbacks = forall m . (MonadIO m, MonadReader MyContext m) => Callbacks { _callback1 :: IORef (m ()), _callback2 :: IORef (m ())}
に新しいコールバックを登録する実際のコードを書くまでは、問題なく動作しているように見えました
Callbacks
。register :: (MonadIO m, MonadReader MyContext m) => m () -> m () register f = do (Callbacks { _callback1 = ref1 }) <- asks _callbacks -- Note the necessary use of pattern matching liftIO $ modifyIORef ref1 (const f)
しかし、次のエラーが発生しました(ここでは簡略化しています):
Could not deduce (m ~ m1) from the context (MonadIO m, MonadReader MyContext m) bound by the type signature for register :: (MonadIO m, MonadReader MyContext m) => m () -> m () or from (MonadIO m1, MonadReader MyContext m1) bound by a pattern with constructor Callbacks :: forall (m :: * -> *). (MonadIO m, MonadReader MyContext m) => IORef (m ()) -> IORef (m ()) -> Callbacks, Expected type: m1 () Actual type: m ()
回避策を見つけることができませんでした。
誰かが私を啓発してくれれば、本当に感謝しています。もしあれば、これを設計する良い方法は何でしょうか?
コメントありがとうございます。
[編集] ysdxの答えを理解している限り、型クラスのm
制約を課すことなくデータ型をパラメータ化しようとしましたが、 ;Callbacks
のインスタンスを作成できませんでした。Data.Default
このようなものを書く:
instance (MonadIO m, MonadReader (MyContext m) m) => Default (Callbacks m) where
def = Callbacks {
_callback1 = {- something that makes explicit use of the Reader layer -},
_callback2 = return ()}
... GHCは次のように不平を言った:
Variable occurs more often in a constraint than in the instance head
in the constraint: MonadReader (MyContext m) m
UndecidableInstances の使用を示唆していますが、理由はわかりませんが、非常に悪いことだと聞きました。使用をあきらめなければならないということData.Default
ですか?