ログや設定のようにかなりグローバルになりがちなものは、IO モナドに入れることをお勧めしますか? (確かに非常に限られたセットの) 例を見ると、Haskell コードは純粋な (つまり、まったくモナドではない) か、IO モナドのどちらかである傾向があると思います。それともこれは誤解ですか?
これは誤解だと思います.IOモナドだけが純粋ではありません. Write/T/Reader/T/State/T/ST モナドのようなモナドは純粋に機能します。このまったく役に立たない例のように、これらのモナドのいずれかを内部的に使用する純粋な関数を書くことができます:
foo :: Int -> Int
foo seed = flip execState seed $ do
modify $ (+) 3
modify $ (+) 4
modify $ (-) 2
これが行っているのは、状態を暗黙的にスレッド化/配管することだけです。手動で明示的に行うことは、ここでの do 表記は、命令的に見えるようにするための優れた構文糖衣を提供するだけです。ここでは IO アクションを実行できません。外部関数を呼び出すこともできません。STモナドを使用すると、純粋な関数インターフェイスを持ちながら、ローカルスコープで実際の可変参照を使用できます。そこではIOアクションを実行できず、純粋に機能します。
いくつかの IO アクションを回避することはできませんが、すべてを IO にフォールバックしたくはありません。なぜなら、そこには何でも行くことができ、ミサイルが発射される可能性があり、制御できないからです。Haskell には、さまざまな程度の安全性/純度で効果的な計算を制御するための抽象化があります。IO モナドは最後の手段にする必要があります (ただし、完全に回避することはできません)。
あなたの例では、モナドトランスフォーマーまたはトランスフォーマーでそれらを構成するのと同じことを行うカスタムメイドのモナドの使用に固執する必要があると思います。私はカスタム モナドを (まだ) 書いたことはありませんが、モナド トランスフォーマーをかなり使用しています (仕事ではなく、私自身のコードです)。 .
モナド変換子を使用するReal World Haskellの章を見たことがありますか?