-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path8_Property_based_testing.sc
More file actions
77 lines (67 loc) · 2 KB
/
8_Property_based_testing.sc
File metadata and controls
77 lines (67 loc) · 2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
opaque type State[S, +A] = S => (A, S)
object State:
// my understanding:
// this is needed to use the opaque type State outside
// otherwise the implementation S => (A, S) is lost
extension [S, A](underlying: State[S, A])
def run(s: S): (A, S) = underlying(s)
// again, this is a "dummy" method
def apply[S, A](f: S => (A, S)): State[S, A] = f
def unit[S, A](a: => A): State[S, A] = {
s => (a, s)
}
extension [S, A, B](underlying: State[S, A])
def map(f: A => B): State[S, B] = {
s => {
val (a, s_new) = underlying.run(s)
(f(a), s_new)
}
}
extension [S, A, B](underlying: State[S, A])
def flatMap(f: A => State[S, B]): State[S, B] = {
s => {
val (a, s_new) = underlying.run(s)
// Note: if we return f(a)
// this would make the resulting return type
// s => State[S, B], i.e. S => State[S, B]
// what we need, however, is S => (B, S)
// therefore, we have:
f(a)(s_new)
}
}
trait RNG:
def nextInt: (Int, RNG)
def nonNegativeInt(rng: RNG): (Int, RNG) =
val (i, r) = rng.nextInt
(if i < 0 then -(i + 1) else i, r)
opaque type Gen[+A] = State[RNG, A]
//object Gen:
// it is not parametric on S, it's fixed to RNG
// def apply[A](f: RNG => (A, RNG)): Gen[A] = f
object Prop:
opaque type FailedCase = String
opaque type SuccessCount = Int
import Prop.{FailedCase, SuccessCount}
trait Prop:
def check: Either[(FailedCase, SuccessCount), SuccessCount]
// Gen[A] is State[RNG, A]
// that is RNG => (A, RNG)
import State.map
def choose(start: Int, stopExclusive: Int): Gen[Int] =
State(rng => nonNegativeInt(rng)).map(
n => start + n % (stopExclusive - start)
)
def unit[A](a: => A): Gen[A] = {
//rng => (a, rng)
State.unit(a)
}
extension [A](self: Gen[A])
def flatMap[B](f: A => Gen[B]): Gen[B] =
rng => {
val (a, rng_next) = self(rng)
f(a)(rng_next)
}
// TODO:
//extension [A](self: Gen[A])
// def listOfN(size: Gen[Int]): Gen[List[A]] =
// size.flatMap()