MonadReaderを定義するためにFunctionalDependencyが必要なのはなぜですか?


8

クラスの定義を理解できた MonadReader

class Monad m => MonadReader r m | m -> r where
...

Haskellの関数型依存関係のドキュメントを読んだ後| m -> r、型変数rがによって一意に決定されることを指定していることが理解できますm。これまでに見たMonadReaderのいくつかの典型的なインスタンス(例:)に基づいて、この要件は妥当だと思いますがReaderReaderこの機能的な依存関係句がなくてもインスタンスを定義できるように思えます。

私の質問は、MonadReaderの定義に機能的な依存関係が必要な理由です。これは、MonadReaderを適切に定義できないという意味で、MonadReaderを定義するために機能的に必要ですか?


これらの質問が提示されたときの楽しい演習は、それらを専門知識のない定義を使用して試して書くことです。
Thomas M. DuBuisson

1
の定義には必要ありませんMonadReader。を便利に使用 するために必要ですMonadReader
ダニエルワグナー

回答:


4

ユーザーにとってより便利な方法で型推論を機能させる必要があります。

たとえば、fundepがないと、これはコンパイルされません。

action :: ReaderT Int IO ()
action = do
  x <- ask
  liftIO $ print x

上記のコンパイルを行うには、次のように記述する必要があります

action :: ReadertT Int IO ()
action = do
  x <- ask :: ReadertT Int IO Int
  liftIO $ print x

fundepせずに、コンパイラはそれを推測することができないためであるxですInt。結局、モナドにReadertT Int IOは複数のインスタンスがあるかもしれません

instance MonadReader Int (ReaderT Int IO) where
   ask = ReaderT (\i -> return i)
instance MonadReader Bool (ReaderT Int IO) where
   ask = ReaderT (\i -> return (i != 0))
instance MonadReader String (ReaderT Int IO) where
   ask = ReaderT (\i -> return (show i))
-- etc.

そのため、プログラマーはを強制する注釈を提供する必要があります。そうしないとx :: Int、コードがあいまいになります。


2

これは実際の答えではありませんが、コメントとしては長すぎます。あなたはMonadReaderfundepなしでクラスを定義することが可能であることは正しいです。特に、各メソッドの型シグネチャはすべてのクラスパラメータを決定します。より細かい階層を定義することは十分に可能です。

class MonadReaderA r m where
  askA :: m r
  askA = readerA id

  readerA :: (r -> a) -> m a
  readerA f = f <$> askA

-- This effect is somewhat different in
-- character and requires special lifting.
class MonadReaderA r m => MonadReaderB r m where
  localB :: (r -> r) -> m a -> m a

class MonadReaderB r m
  => MonadReader r m | m -> r

ask :: MonadReader r m => m r
ask = askA

reader
  :: MonadReader r m
  => (r -> a) -> m a
reader = readerA

local
  :: MonadReader r m
  => (r -> r) -> m a -> m a
local = localB

このアプローチの主な問題は、ユーザーが大量のインスタンスを作成する必要があることです。


1

混乱の原因は、

class Monad m => MonadReader r m | m -> r where
  {- ... -}

自身をm含むことが暗黙的に想定されていrます(一般的なインスタンスの場合)。私は軽いのdefiniton使用してみましょうReaderとしてを

newtype Reader r a = Reader {runReader :: r -> a}

ときにrパラメータが選択されますがeaselyのためのモナドのインスタンスを定義することができますReader r。つまり、型クラスの定義mはの代わりに使用する必要がありますReader r。だから、表現が最終的にどのようになるかを見てみましょう:

instance MonadReader r (Reader r) where -- hey!! r is duplicated now
  {- ... -}                             -- The functional dependecy becomes Reader r -> r which makes sense

しかし、なぜこれが必要なのでしょうか。クラスask内のの定義を見てくださいMonadReader

class Monad m => MonadReader r m | m -> r where
  ask :: m r -- r and m are polymorphic here
  {- ... -}

fun-depがなければ、ask別のタイプを状態として返すように定義するために私を止めることはできません。さらに、自分のタイプのモナドリーダーの多くのインスタンスを定義できます。例として、これはfunc-depなしの有効な定義になります

instance MonadReader Bool (Reader r) where
--                   ^^^^         ^
--                   |            |- This is state type in the user defined newtype 
--                   |- this is the state type in the type class definition
  ask :: Reader r Bool
  ask = Reader (\_ -> True) -- the function that returns True constantly
  {- ... -}                             
instance MonadReader String (Reader r) where
--                   ^^^^^^         ^
--                   |              |- This is read-state type in the user defined newtype 
--                   |- this is the read-state type in the type class definition
  ask :: Reader r String
  ask = Reader (\_ -> "ThisIsBroken") -- the function that returns "ThisIsBroken" constantly
  {- ... -}                             

したがって、もし私が値を持っていたらval :: ReaderT Int IO Double、結果はどうなるでしょうask。以下のように型シグネチャを指定する必要があります

val :: Reader Int Double
val = do
  r <- ask :: Reader Int String
  liftIO $ putStrLn r   -- Just imagine you can use liftIO
  return 1.0

> val `runReader` 1
"ThisIsBroken"
1.0

val :: Reader Int Double
val = do
  r <- ask :: Reader Int Bool
  liftIO $ print r   -- Just imagine you can use liftIO
  return 1.0

> val `runReader` 1
True
1.0

意味がないことは別として、型を何度も指定するのは不便です。

の実際の定義を使用した結論としてReaderT。あなたが何か持っている場合はval :: ReaderT String IO Int、機能の依存関係を言うようなタイプがのただ1つのインスタンスかもしれないMonadReader型クラスを使用していることを一つであると定義されているStringとして、r

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.