3

私は、ユーザーがimport DryRun最初にプログラムを実行し、次にimport Doすべてが正しいと思った場合にプログラムを実行できるようにしたいのです。
したがって、実際の作業を行うモジュールがあります。

doThis ∷ SomeStack ()
doThis = actuallyDoThis
...
doThat ∷ SomeStack ()
doThat = actuallyDoThat

恥ずかしがり屋のユーザーのためのモジュール:

doThis ∷ SomeStack ()
doThis = liftIO $ putStrLn "DoThis"
...
doThat ∷ SomeStack ()
doThat = liftIO $ puStrlLn "DoThat"

Do問題は、インターフェイスがDryRun同じであるかどうかを確認できないことです(コンパイラは役に立ちません)。この混乱は、開発中に維持するのが困難です。
この種の問題を解決するための一般的なイディオムはありますか?

4

1 に答える 1

2

問題のモジュールを具体化できます。つまり、基本モジュールでデータ型を定義します。

module Base where
data MyModule = MyModule {
    doThis_ :: SomeStack (),
    doThat_ :: SomeStack ()
}

各モジュールをエクスポートする必要があるもの (アンダースコアの理由はすぐに明らかになります)。

次に、各モジュールでこのタイプの値を定義できます。たとえば、次のようになります。

module DryRun(moduleImpl) where
import Base
moduleImpl :: MyModule
moduleImpl = MyModule doThis doThat
-- doThis and doThat defined as above, with liftIO . putStrLn

module Do(moduleImpl) where
import Base
moduleImpl :: MyModule
moduleImpl = MyModule doThis doThat
-- doThis and doThat defined as above, where the real work gets done

MyModuleレコード構文を使用して値を構築しないのは、MyModule変更された場合に型チェッカーがほとんどの場合に文句を言い始めることを確認するためです。

クライアントモジュールでできること

module Client where
import DryRun
-- import Do -- uncomment as needed

doThis = doThis_ moduleImpl
doThat = doThat_ moduleImpl

-- do whatever you want here

これで、同じ操作が両方のモジュールによってエクスポートされることがわかりました。確かにこれは面倒で扱いにくいですが、Haskell にはファーストクラスのモジュールがないため、常にモジュール システムの制限を回避する必要があります。良い点は、Base モジュールと Client モジュールを 1 回記述するだけでよく、MyModule値を操作するコンビネータの定義を開始できることです。例えば

doNothing = MyModule (return ()) (return ())
addTracing impl = MyModule ((liftIO $ putStrLn "DoThis") >> doThis_ impl) 
                           ((liftIO $ putStrLn "DoThat") >> doThat_ impl)

私が間違っていなければ、DryRunモジュールの実装を に置き換えることができます。addTracing doNothing

于 2012-04-28T15:01:46.760 に答える