を使用しpipes
ます。ライブラリはまだ比較的新しいため、慣用的であるとは言いませんが、問題を正確に解決すると思います。
たとえば、あるデータベースへのインターフェースをラップしたいとしましょう:
import Control.Proxy
-- This is just some pseudo-code. I'm being lazy here
type QueryString = String
type Result = String
query :: QueryString -> IO Result
database :: (Proxy p) => QueryString -> Server p QueryString Result IO r
database = runIdentityK $ foreverK $ \queryString -> do
result <- lift $ query queryString
respond result
次に、データベースへの 1 つのインターフェイスをモデル化できます。
user :: (Proxy p) => () -> Client p QueryString Result IO r
user () = forever $ do
lift $ putStrLn "Enter a query"
queryString <- lift getLine
result <- request queryString
lift $ putStrLn $ "Result: " ++ result
次のように接続します。
runProxy $ database >-> user
これにより、ユーザーはプロンプトからデータベースと対話できるようになります。
次に、モック データベースを使用してデータベースを切り替えることができます。
mockDatabase :: (Proxy p) => QueryString -> Server p QueryString Result IO r
mockDatabase = runIdentityK $ foreverK $ \query -> respond "42"
これで、モック用のデータベースを非常に簡単に切り替えることができます。
runProxy $ mockDatabase >-> user
または、データベース クライアントを切り替えることもできます。たとえば、特定のクライアント セッションが奇妙なバグを引き起こしたことに気付いた場合、次のように再現できます。
reproduce :: (Proxy p) => () -> Client p QueryString Result IO ()
reproduce () = do
request "SELECT * FROM WHATEVER"
request "CREATE TABLE BUGGED"
request "I DON'T REALLY KNOW SQL"
...次に、次のように接続します。
runProxy $ database >-> reproduce
pipes
では、ストリーミングまたはインタラクティブな動作をモジュラー コンポーネントに分割できるため、好きなように組み合わせて組み合わせることができます。これが依存性注入の本質です。
の詳細については、 Control.Proxy.Tutorialpipes
のチュートリアルを参照してください。