[解決済み] ハスケルでControl.Monad.Writerを遊ぶには?
質問
私は関数型プログラミングの初心者で、最近、以下のサイトで学びました。 ハスケルを学ぶ で学習中ですが この章 を読み進めると、下のようなプログラムに引っかかってしまいました。
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = Writer (x, ["Got number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (a*b)
これらの行を .hs ファイルに保存しましたが、ghci にインポートするのに失敗し、文句を言われました。
more1.hs:4:15:
Not in scope: data constructor `Writer'
Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.
":info"コマンドで種類を調べました。
Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
-- Defined in `Control.Monad.Trans.Writer.Lazy'
私の見解では、これは "newtype Writer w a ..." のようなものであるはずでした。 ということで、データコンストラクタに与えてWriterを取得する方法について混乱しています。
バージョンの問題だと思うのですが、私のghciのバージョンは7.4.1です。
どのように解決するのですか?
パッケージ
Control.Monad.Writer
はデータコンストラクタをエクスポートしません。
Writer
. LYAHが書かれた当時はこれが違っていたのでしょう。
ghci で MonadWriter タイプクラスを使う
代わりに、ライターを作成するには
writer
関数でライターを作成します。たとえば、ghci セッションでは、次のようにします。
ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])
現在
logNumber
はライターを作成する関数です。その型を求めることができます。
ghci> :t logNumber
logNumber :: (Show a, MonadWriter [String] m) => a -> m a
ということは、推論された型は、関数が返す
特定
を実装しているものであるということです。
MonadWriter
型クラスを実装しているものです。これで使えるようになりました。
ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) }
:: Writer [String] Int
(と入力します(実際には1行にまとめて入力します)。ここでは
multWithLog
を
Writer [String] Int
. これで実行できるようになりました。
ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])
そして、中間処理をすべてログに記録していることがわかります。
なぜこのようなコードが書かれているのでしょうか?
なぜわざわざ
MonadWriter
型クラスをわざわざ作るのでしょうか?その理由は、モナド変換に関係しています。あなたが正しく理解したように、最も簡単な実装方法は
Writer
を実装する最も簡単な方法は、ペアの上に新しい型のラッパーとして実装することです。
newtype Writer w a = Writer { runWriter :: (a,w) }
このためにモナドインスタンスを宣言し、関数を書くと
tell :: Monoid w => w -> Writer w ()
これは単に入力をログに記録するものです。ここで、ロギング機能を持ちながら、他のこともできるモナドが欲しいとします。この場合、次のように実装します。
type RW r w a = ReaderT r (Writer w a)
ここで、ライターは
ReaderT
モナドトランスフォーマーの中にあるため、出力をログに記録したい場合は
tell w
(を使うことはできません(これはラップされていないライターに対してのみ動作するからです)。
lift $ tell w
を解除します。
tell
関数を通して
ReaderT
を通して、内側のライターモナドにアクセスできるようにします。もし2層のトランスフォーマーが必要なら(例えばエラー処理も追加したい場合)、そのためには
lift $ lift $ tell w
. これはすぐに扱いにくくなります。
代わりに、型クラスを定義することで、ライターの周りの任意のモナド変換ラッパーをライター自体のインスタンスにすることができます。例えば
instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)
というのは、もし
w
がモノイドであり
m
は
MonadWriter w
であれば
ReaderT r m
はまた
MonadWriter w
. これはつまり
tell
関数を、モナド変換器を通して明示的に持ち上げることに煩わされることなく、変換されたモナド上で直接使用することができることを意味します。
関連
-
[解決済み】haskellでリストを逆順にする
-
[解決済み】Haskellでの挿入ソート
-
[解決済み] Hindley-Milnerのどの部分が理解できないのでしょうか?
-
[解決済み] Project Eulerとの速度比較。CとPythonとErlangとHaskellの比較
-
[解決済み】Haskellの入門編
-
[解決済み] Haskellの "Just "構文とは?
-
[解決済み] GHCでコンパイルした小さなHaskellプログラムを巨大なバイナリにする
-
[解決済み] Haskellデータ型のメモリフットプリント
-
[解決済み] Real World Haskellのどの部分が今となっては時代遅れ、あるいはバッドプラクティスと考えられているのでしょうか?
-
[解決済み] Haskell の現在のモジュールにインポートモジュールを追加してエクスポートする。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Haskellは実世界で何に使われているのか?[クローズド]
-
[解決済み] Haskellにはなぜ "data "と "newtype "があるのですか?重複] [重複] [重複
-
[解決済み] GHCiの複数行コマンド
-
[解決済み] 無限リストでのfoldlとfoldrの動作
-
[解決済み] Emacs Interactive-Haskell repl は、cabal と working directory のいずれかが project directory に設定されると無応答になる。
-
[解決済み] このフィボナッチ関数はどのようにメモされているのですか?
-
[解決済み] Rank2Typesの目的は何ですか?
-
[解決済み] Real World Haskellのどの部分が今となっては時代遅れ、あるいはバッドプラクティスと考えられているのでしょうか?
-
[解決済み] Haskell の現在のモジュールにインポートモジュールを追加してエクスポートする。
-
[解決済み] Haskellのドット演算子:もっと説明が欲しい