Test.LeanCheck.Core
はコメントを除いて 185 行くらい)> holds 100 $ \xs-> sort (sort xs) == sort (xs :: [Int])
True
Show
クラスのインスタンスを提供している(個人的には下の二つが面白いと思った)
(leancheck4s という名前が
当時はなぜか思い付かなかった‥‥)
Int
などの整数、Double
などの浮動小数点数、scala> import codes.quine.leanprops._
import codes.quine.leanprops._
scala> inspect { (x: Boolean, y: Boolean) => x && y }
res0: String =
((x, y) => (x, y) match {
case (true, true) => true
case _ => false
})
scala> holds(100) { (xs: List[Int]) =>
| xs.sorted.sorted == xs.sorted
| }
res1: Boolean = true
trait Listable[A]
:A
が自動生成可能なことを表す型クラスtrait Inspectable[A]
:A
が文字列にして表示可能なことを表す型クラスclass Tiers[A]
:Stream[Seq[A]]
をラップしたデータ構造class WithInspectConfig[A]
:InspectConfig
を持った Reader Monad一番重要なのはtrait Listable[A]
。
trait Listable[A]
trait Listable[A] {
def tiers: Tiers[A]
}
Tiers[A]
はStream[Seq[A]]
のラッパStream[A]
ではなくStream[Seq[A]]
なのはStream[A]
じゃダメ?仮にListable[A]
がこうだったとする。
trait Listable[A] {
def list: Stream[A]
}
list[A] = Listable[A].list
とする。
Stream[A]
じゃダメ?list[Int] = Stream(0, 1, -1, ...)
そこでlist[(A, B)]
を考えると、単純な実装だと‥
list[(A, B)] = for {
x <- list[A]
y <- list[B]
} yield (x, y)
Stream[A]
じゃダメ?つまり、
Stream((list[A](0), list[B](0)),
(list[A](0), list[B](1)),
... (list[B]が終了するまで続く),
(list[A](1), list[B](0)),
...)
(Int, Int)
の場合、
左側に0
しか出てこなくなる。
Stream[A]
じゃダメ?そこでtiers[A]: Stream[Seq[A]]
を考える。
tiers[Int] = Stream(Seq(0), Seq(1), Seq(-1), ...)
tiers[(A, B)]
は次のような感じ、
def prod[A, B](xs: Seq[A], ys: Seq[B]) = for (x <- xs; y <- ys) yield (x, y)
Stream(prod(tiers[A](0), tiers[B](0)), // 添字の合計が0
prod(tiers[A](0), tiers[B](1))
++ prod(tiers[A](1), tiers[B](0)), // 添字の合計が1
prod(tiers[A](0), tiers[B](2))
++ prod(tiers[A](1), tiers[B](1))
++ prod(tiers[A](2), tiers[B](0)), // 添字の合計が2
...)
こうすると、無限リストでもいい感じに積を取れる。
trait Listable[A]
Set
やMap
の場合は重複しないように生成したり、Function1
の場合はMap
とほとんど同じなのだけど、(さすがに時間が無さそうなので三行で)
_
で上手い具合に置き換えていく引数を自動生成できるんだから、
たくさん呼び出して返り値を記録すれば
それっぽいmatch
式が作れるよね、というノリ。
trait Inspectable[A]
trait Inspectable[A] {
def inspect(x: A): WithInspectConfig[A]
def bindtiers(x: Try[A]): Tiers[(Seq[Seq[String]], Try[String])]
}
このbindtiers
が、引数と返り値を記録するメソッド。
scala> inspect { (xs: List[Int]) => xs.head }
res3: String =
(x => x match {
case List() => throw new NoSuchElementException("head of empty list")
case List(0) => 0
case List(0, 0) => 0
case List(1) => 1
case List(0, 0, 0) => 0
case List(0, 1) => 0
case List(1, 0) => 1
case List(-1) => -1
...
})
ぶっちゃけBoolean
のときくらいしか上手くいかない。
Stream
は難しすぎる。WithInspectConfig
って名前どうなの?Listable
、Inspectable
のインスタンスを増やすcats
・scalaz
の型のインスタンスを実装するshapeless
での自動導出sbt
との統合discipline
のようなインターフェース(?)