2017-06-15 11 views
3

私はしばらくの間、IOモナドを把握しようとしてきました。私が誤解されていない場合、目標は副作用の説明と実際の実行を分割することです。以下の例のように、Scalaは、参考にトランスパランではない環境変数を取得する方法を持っています。 2つの質問が発生しました。猫効果とIOモナド

質問1:この1参照上transparant

質問2:どのように(単位/プロパティベース)を適切には、これをテスト?メモリ参照をチェックし、内部関数をチェックすることができないので、等価性をチェックすることはできません。なぜなら、誤っていないと関数の比較ができないからです。しかし、私は単体テストで実際の副作用を実行したくありません。また、IOモナドの設計ミスや誤用ですか?

case class EnvironmentVariableNotFoundException(message: String) extends Exception(message) 

object Env { 
    def get(envKey: String): IO[Try[String]] = IO.unit.flatMap((_) => IO.pure(tryGetEnv(envKey))) 

    private[this] def tryGetEnv(envKey: String): Try[String] = 
    Try(System.getenv(envKey)) 
     .flatMap(
     (x) => 
      if (x == null) Failure(EnvironmentVariableNotFoundException(s"$envKey environment variable does not exist")) 
      else Success(x) 
    ) 

}

答えて

2

それはちょうどあなたの例では、システムコールのような不純なソースから来ている、あなたのプログラム内の値を、包むためにIOを使用することが良いことです。これにより、IO[A]が返されます。「」は、Aを不純な手段で取得することができます "です。その後、mapflatMapなどを介してAで動作する純粋な/参照可能な透明な関数を使用できます。

これは2つの答えにつながります。私は、あなたがテストしようとしているのはどのプロパティですか?

このコードを見ると、tryGetEnvflatMapがテストを保証するのに十分なほど複雑なものであることがわかりました。このロジックを純粋な関数に抽出することで、これを行うことができます。これを書き直すなどして、IO[String]を返す関数があり、これを目的の型に変換する(テスト済みの)関数を書くことができます。

IOはあなたの言うこととまったく同じですが、明示的にコードを明確にすることは含まれていません!ここで実際の副作用をテストする場合は、IOを使用しないプログラムと同じように、Systemを引数として渡してテスト用に偽装することを検討することもできます。

ので要約すると、私は(この場合は、IO[Try[String]]IO[A]を作成するために、Systemへの呼び出しを行い最小限機能を作成することを検討したいです。この最小限の関数を嘲笑でテストすることもできますが、そうすることによって価値を追加していると感じる場合にのみ選択できます。この周辺では、Aを持つ関数を記述し、これらの関数に純粋な値を渡してテストすることができます。覚えておいてくださいmapの署名はIOで、fは純粋な(テスト可能な)関数です!その極端に運ば

sealed abstract class IO[+A] { 

    def map[B](f: A => B): IO[B] 
      ^f is a pure function! 
       test it by passing A values and verifying the Bs 

、このパターンは、あなたのプログラム(例えば、あなたのmain機能)の非常にエッジにIO値を作成することを奨励しています。残りのプログラムは、プログラムの実行時にIO型から来る値に作用する純粋な関数から作成できます。

関連する問題