2

ScalaCheck でケース クラスを生成するために、次のようなイディオムを使用していました。

GenSomething.map2(GenSomethingElse)(MyClass(_, _))

最近、ScalaCheck を 1.11 にアップグレードし、mapNメソッドを削除しました。各フィールドのジェネレーターに中間名を割り当てる必要がないようにしたいのですが、mapNメソッドはそれを行う最も簡単な方法を提供しました。現在、最適な構文は次のとおりです。

for {
  something <- GenSomething
  somethingElse <- GenSomethingElse
} yield MyClass(
  something = something,
  somethingElse = somethingElse)

それはそれほど悪くはありません (構造体はコンストラクターの引数の数が少ないため)、ここで特別なことは何も行われていないことを明確にしたいと思います。リーダーなしで各引数のジェネレーターを指定しているだけですそれを確認するためにコードを読む必要があります。

要するに、アプリケーション構文に似たものが欲しいのです。残念ながら、scalaz、shapeless、またはマクロを使用するオプションはありません。この最後の文は、「X を実行できるものにアクセスせずに X を実行するにはどうすればよいか」という私の疑問をほとんど引き起こしていることは理解していますが、誰かが良い考えを持っていることを願っています。

4

1 に答える 1

0

ボイラープレートを防ぐためのライブラリを明示的に除外しているため、いくつかのボイラープレートを使用する必要があります。

Gen.resultOf と同様のアプローチを使用して、アリティごとに gen コンバイナーを定義できます。実際には、Gen.resultOf をそのまま使用できます。resultOf との唯一の違いはGen、暗黙的に提供された s ではなく、明示的に提供された s が必要なことですArbitrary

object GenCombiner {

  def zipMap[A, R](a: Gen[A])(f: A ⇒ R): Gen[R] =
    Gen.resultOf(f)(Arbitrary(a))

  def zipMap[A, B, R](a: Gen[A], b: Gen[B])(f: (A, B) ⇒ R): Gen[R] =
    Gen.resultOf(f)(Arbitrary(a), Arbitrary(b))

  def zipMap[A, B, C, R](a: Gen[A], b: Gen[B], c: Gen[C])(f: (A, B, C) ⇒ R): Gen[R] =
    Gen.resultOf(f)(Arbitrary(a), Arbitrary(b), Arbitrary(c))

  // other arities
}

object GenCombinerTest {
  import GenCombiner._

  case class Foo(alpha: String, num: String)

  val fooGen: Gen[Foo] = zipMap(Gen.alphaStr, Gen.numStr)(Foo)
}
于 2015-11-03T10:46:24.897 に答える