「ステートフルモナドなし」という `MonadUnliftIO`の要件を理解するにはどうすればよいですか?

bbarker

私が上で見てきたhttps://www.fpcomplete.com/blog/2017/06/tale-of-two-brackets一部をスキミングけれども、私はまだかなり問題の核心は、「理解していないStateT悪い、IOですOK」、Haskellが悪いStateTモナドを書くことを漠然と許可しているという感覚を得る以外に(または、記事の最終的な例では、MonadBaseControlではなくStateT)。

ハドックでは、次の法則を満たす必要があります。

askUnliftIO >>= (\u -> liftIO (unliftIO u m)) = m

したがって、これはm、を使用するときにモナドで状態が変更されないことを示しているように見えますaskUnliftIOしかし、私の考えではIO、では、全世界が国家になることができます。たとえば、ディスク上のテキストファイルの読み取りと書き込みを行うことができます。

Michaelによる別の記事を引用するには

偽りの純度WriterTとStateTは純粋であり、技術的には純粋であると言います。ただし、正直に言うと、StateT内に完全に存在するアプリケーションがある場合、純粋なコードから必要な抑制されたミューテーションのメリットは得られません。スペードをスペードと呼び、可変変数があることを受け入れることもできます。

これは確かにそうだと私に思わせます。IOでは私たちは正直でありStateT、では、可変性について正直ではありません...しかし、それは上記の法律が示していることとは別の問題のようです。結局のところ、MonadUnliftIOを想定していIOます。IO他のものよりも制限がどのようになっているのかを概念的に理解するのに苦労しています。

アップデート1

寝た後(一部)、私はまだ混乱していますが、日が経つにつれて徐々に少なくなっています。の法定証明を作成しましたIOidREADMEにその存在を実感しました特に、

instance MonadUnliftIO IO where
  askUnliftIO = return (UnliftIO id)

したがってaskUnliftIOIO (IO a)を返すように見えUnliftIO mます。

Prelude> fooIO = print 5
Prelude> :t fooIO
fooIO :: IO ()
Prelude> let barIO :: IO(IO ()); barIO = return fooIO
Prelude> :t barIO
barIO :: IO (IO ())

法則に戻ると、m変換されたモナド(askUnliftIO)でラウンドトリップを実行すると、モナドでは状態が変化しないと言っているように見えますここで、ラウンドトリップはunLiftIO->liftIOです。

、上記の例を再開barIO :: IO ()私たちはそうならば、barIO >>= (u -> liftIO (unliftIO u m))そして、u :: IO ()そしてunliftIO u == IO ()、その後、liftIO (IO ()) == IO ()**基本的にすべてが内部でのアプリケーションidであるため、を使用していても状態が変更されていないことがわかりますIO重要なのは、をa使用した結果として、の値が実行されたり、他の状態が変更されたりしないことですaskUnliftIOもしそうなら、の場合のように、それrandomIO :: IO aを実行askUnliftIOなければ同じ値を取得することはできません(以下の検証試行1)

しかし他のモナドが状態を維持していても、同じことができるようです。しかし、一部のモナドでは、そうすることができない場合があることもわかります。不自然な例を考えてみましょう。aステートフルモナドに含まれるタイプの値にアクセスするたびに、一部の内部状態が変更されます。

検証の試み1

> fooIO >> askUnliftIO
5
> fooIOunlift = fooIO >> askUnliftIO
> :t fooIOunlift
fooIOunlift :: IO (UnliftIO IO)
> fooIOunlift
5

これまでのところ良好ですが、次のことが発生する理由について混乱しています。

 > fooIOunlift >>= (\u -> unliftIO u)

<interactive>:50:24: error:
    * Couldn't match expected type `IO b'
                  with actual type `IO a0 -> IO a0'
    * Probable cause: `unliftIO' is applied to too few arguments
      In the expression: unliftIO u
      In the second argument of `(>>=)', namely `(\ u -> unliftIO u)'
      In the expression: fooIOunlift >>= (\ u -> unliftIO u)
    * Relevant bindings include
        it :: IO b (bound at <interactive>:50:1)
エフリオン

「StateTは悪いです、IOはOKです」

それは実際には記事のポイントではありません。この考え方はMonadBaseControl、並行性と例外が存在する場合に、ステートフルモナド変換子との紛らわしい(そしてしばしば望ましくない)動作を許可するというものです

finally :: StateT s IO a -> StateT s IO a -> StateT s IO a良い例です。StateTタイプの可変変数をsモナドにアタッチしているm」というメタファーを使用するs場合、例外がスローされたときにファイナライザーアクションが最新の値にアクセスすることを期待できます

forkState :: StateT s IO a -> StateT s IO ThreadId別のものです。入力からの状態の変更が元のスレッドに反映されることを期待するかもしれません。

lol :: StateT Int IO [ThreadId]
lol = do
  for [1..10] $ \i -> do
    forkState $ modify (+i)

これlolは(モジュロパフォーマンス)として書き直すことができると思われるかもしれませんmodify (+ sum [1..10])しかし、それは正しくありません。の実装はforkState、初期状態をフォークされたスレッドに渡すだけで、状態の変更を取得することできませんの簡単で一般的な理解はStateTここでは失敗します。

代わりに、StateT s m as計算によって暗黙的にスレッド化されるタイプのスレッドローカル不変変数を提供するトランスフォーマーとして、より微妙な見方を採用する必要があります。そのローカル変数を同じタイプの新しい値に置き換えることができます。計算の将来のステップのために。」(多かれ少なかれ、の冗長な英語の言い回しs -> m (a, s))この理解により、の動作はfinallyもう少し明確なります。これはローカル変数であるため、例外に耐えることはできません。同様に、forkStateより明確なります。これはスレッドローカル変数であるため、別のスレッドに変更しても他のスレッドには影響しません。

これは時々あなたが望むものです。しかし、それは通常、人々がコードIRLを書く方法ではなく、しばしば人々を混乱させます。

長い間、この「下げる」操作を実行するためのエコシステムのデフォルトの選択はでしたがMonadBaseControl、これには多くの欠点がありました。タイプがわかりにくい、インスタンスを実装するのが難しい、インスタンスを導出できない、動作がわかりにくい場合があります。素晴らしい状況ではありません。

MonadUnliftIO物事をより単純なモナド変換子のセットに制限し、比較的単純な型、派生可能なインスタンス、および常に予測可能な動作を提供できます。コストはつまりExceptTStateTなど変圧器はそれを使用することはできません。

基本的な原則は、可能なことを制限することにより、何が起こるかを理解しやすくすることです。MonadBaseControlは非常に強力で一般的であり、その結果、使用が非常に難しく、混乱を招きます。MonadUnliftIOそれほど強力ではなく一般的ですが、はるかに使いやすいです。

したがって、これは、askUnliftIOを使用するときに、モナドmで状態が変更されないことを示しているように見えます。

これは真実ではありません-法律はunliftIO、モナド変換子をに下げる以外は何もしはならないと述べていますIOこれがその法則に違反するものです:

newtype WithInt a = WithInt (ReaderT Int IO a)
  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader Int)

instance MonadUnliftIO WithInt where
  askUnliftIO = pure (UnliftIO (\(WithInt readerAction) -> runReaderT 0 readerAction))

これが与えられた法則に違反していることを確認しましょう:askUnliftIO >>= (\u -> liftIO (unliftIO u m)) = m

test :: WithInt Int
test = do
  int <- ask
  print int
  pure int

checkLaw :: WithInt ()
checkLaw = do
  first <- test
  second <- askUnliftIO >>= (\u -> liftIO (unliftIO u test))
  when (first /= second) $
    putStrLn "Law violation!!!"

によって返される値testaskUnliftIO ...下げ/持ち上げが異なるため、法律に違反しています。さらに、観察された効果は異なり、それも素晴らしいことではありません。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Jasmineでユニットテストを行う場合、「サブ要件」のないファイルを要求するにはどうすればよいですか?

分類Dev

独自の理解に準拠したscalaモナドを作成するにはどうすればよいですか?

分類Dev

ロードする要件ファイルをreadthedocsに伝えるにはどうすればよいですか?

分類Dev

要件ファイルを使用してモジュールをtoxでインストールするにはどうすればよいですか?

分類Dev

複数の要件ファイルを定義するにはどうすればよいですか?

分類Dev

以下の要件で描画可能な円を作成するにはどうすればよいですか?

分類Dev

正規表現を使用してこの要件を検証するにはどうすればよいですか

分類Dev

elseステートメントをdictの理解に追加するにはどうすればよいですか?

分類Dev

「古いモード100755新しいモード100644」というファイルをGitのステージングされていない変更から削除するにはどうすればよいですか?

分類Dev

レーキテストでデフォルトのミニテストを使用しないようにするにはどうすればよいですか?

分類Dev

setMyLocationEnabled権限の要件を解決するにはどうすればよいですか?

分類Dev

アプリの最小RAM要件を指定するにはどうすればよいですか

分類Dev

Djangoのビューでサイト全体のパスワード保護と特権要件を設定するにはどうすればよいですか?

分類Dev

ワークフローを使用してHPALMで要件とTestSetFolderのパスを取得するにはどうすればよいですか?

分類Dev

パスワード要件を変更するにはどうすればよいですか?

分類Dev

hashicorpのボールト監査から何かを理解するにはどうすればよいですか?

分類Dev

別のモデルの要件に基づいてDjangoモデルをフィルタリングするにはどうすればよいですか?

分類Dev

JsTree3.1.1の最小ブラウザ要件を見つけるにはどうすればよいですか。

分類Dev

デフォルトストリームをクエリするときのcudaStreamQueryの動作を理解するにはどうすればよいですか?

分類Dev

`(`、 `)`と `test`に関するこのPOSIXの説明を理解するにはどうすればよいですか?

分類Dev

モジュールのインポート名をそのpip要件名から取得するにはどうすればよいですか?

分類Dev

xcodeのビルドフェーズとしてリモートスクリプトを実行するにはどうすればよいですか?

分類Dev

iTunes Connectの要件に合うようにpngファイルのサイズを変更するにはどうすればよいですか?

分類Dev

TensorFlowで「テンソル」という用語を理解するにはどうすればよいですか?

分類Dev

PyMCモデルで「yield」の使用を理解するにはどうすればよいですか?

分類Dev

setup.pyで実際に必要な要件を判断するにはどうすればよいですか?

分類Dev

各モデルのkerasの `preprocess_input`関数を理解するにはどうすればよいですか?

分類Dev

動的要件を外部として追加するにはどうすればよいですか?

分類Dev

MySQLから大量のデータをロードしてテキストファイルとして保存するにはどうすればよいですか?

Related 関連記事

  1. 1

    Jasmineでユニットテストを行う場合、「サブ要件」のないファイルを要求するにはどうすればよいですか?

  2. 2

    独自の理解に準拠したscalaモナドを作成するにはどうすればよいですか?

  3. 3

    ロードする要件ファイルをreadthedocsに伝えるにはどうすればよいですか?

  4. 4

    要件ファイルを使用してモジュールをtoxでインストールするにはどうすればよいですか?

  5. 5

    複数の要件ファイルを定義するにはどうすればよいですか?

  6. 6

    以下の要件で描画可能な円を作成するにはどうすればよいですか?

  7. 7

    正規表現を使用してこの要件を検証するにはどうすればよいですか

  8. 8

    elseステートメントをdictの理解に追加するにはどうすればよいですか?

  9. 9

    「古いモード100755新しいモード100644」というファイルをGitのステージングされていない変更から削除するにはどうすればよいですか?

  10. 10

    レーキテストでデフォルトのミニテストを使用しないようにするにはどうすればよいですか?

  11. 11

    setMyLocationEnabled権限の要件を解決するにはどうすればよいですか?

  12. 12

    アプリの最小RAM要件を指定するにはどうすればよいですか

  13. 13

    Djangoのビューでサイト全体のパスワード保護と特権要件を設定するにはどうすればよいですか?

  14. 14

    ワークフローを使用してHPALMで要件とTestSetFolderのパスを取得するにはどうすればよいですか?

  15. 15

    パスワード要件を変更するにはどうすればよいですか?

  16. 16

    hashicorpのボールト監査から何かを理解するにはどうすればよいですか?

  17. 17

    別のモデルの要件に基づいてDjangoモデルをフィルタリングするにはどうすればよいですか?

  18. 18

    JsTree3.1.1の最小ブラウザ要件を見つけるにはどうすればよいですか。

  19. 19

    デフォルトストリームをクエリするときのcudaStreamQueryの動作を理解するにはどうすればよいですか?

  20. 20

    `(`、 `)`と `test`に関するこのPOSIXの説明を理解するにはどうすればよいですか?

  21. 21

    モジュールのインポート名をそのpip要件名から取得するにはどうすればよいですか?

  22. 22

    xcodeのビルドフェーズとしてリモートスクリプトを実行するにはどうすればよいですか?

  23. 23

    iTunes Connectの要件に合うようにpngファイルのサイズを変更するにはどうすればよいですか?

  24. 24

    TensorFlowで「テンソル」という用語を理解するにはどうすればよいですか?

  25. 25

    PyMCモデルで「yield」の使用を理解するにはどうすればよいですか?

  26. 26

    setup.pyで実際に必要な要件を判断するにはどうすればよいですか?

  27. 27

    各モデルのkerasの `preprocess_input`関数を理解するにはどうすればよいですか?

  28. 28

    動的要件を外部として追加するにはどうすればよいですか?

  29. 29

    MySQLから大量のデータをロードしてテキストファイルとして保存するにはどうすればよいですか?

ホットタグ

アーカイブ