2TQRGTV[$CUGF6GUVKPI 
YKVJ5ECNC%JGEM 
CXKF)CNKEJGV 
(TGGNCPEG(WPEVKQPCN2TQITCOOGT 
6YKVVGTFICNKEJGV
5EJGFWNG 
 .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU 
 (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 
 6GUVECUGIGPGTCVKQP 
 GHKPKPICRTQITCORTQRGTVKGU 
 +PVTQFWEKPI#NIGDTCKEGUKIP 
 6JGECUGQH/QPQKF
1RVKQPUVQEJGEMEQTTGEVPGUUQH 
CRTQITCO 
ō 6GUVTKXGPGXGNQROGPVVGEJPKSWGU
Z7PKVŎ 
ō $GJCXKQTTKXGPGXGNQROGPV 
ō 2TQRGTV[$CUGF6GUVKPI 
ō 6[RG TKXGP GXGNQROGPV GRGPFGPV 6[RGU 
6JGQTGO 2TQXGTU 2CTCOGVTKEKV[ 
6JGQTGOU HQT 
(TGGŎ
6GUVTKXGPGXGNQROGPV 
ō #RTQITCOKUXGTKHKGFD[UGXGTCNWPKVVGUVU 
ō 2TQITCOCPFVGUVUCTGFGXGNQRGFKVGTCVKXGN[ 
ō 6GUVU CTG EQPETGVG GZCORNGU QH JQY C RTQITCO 
UJQWNFRGTHQTOKPCRCTVKEWNCTUKVWCVKQP 
ō 6JG[FQPņVIKXGCUUWTCPEGVJCVVJGQXGTCNNDGJCXKQT 
QHCRTQITCOKUEQXGTGF
$GJCXKQTTKXGPGXGNQROGPV 
ō 5RGEKHKECVKQPU CTG FGHKPGF WUKPI C PCVWTCN 
HQTOCNKUO VJCV KU GZGEWVCDNG CPF ECP XGTKH[ C 
RTQITCO 
ō 6GUVU OWUV DG GZJCWUVKXG VQ EQXGT VJG QXGTCNN 
RTQITCODGJCXKQT
2TQRGTV[$CUGF6GUVKPI 
ō 2TQRGTV[$CUGF6GUVKPICNNQY[QWVQFGUETKDG[QWT 
RTQITCO RTQRGTVKGU NGVVKPI VJG WPFGTN[KPI 
HTCOGYQTM IGPGTCVG VGUV ECUGU CPF XGTKH[ 
RTQRGTVKGU 
ō #RTQRGTV[KUCPCDUVTCEVURGEKHKECVKQP 
ō )GPGTCVGFVGUVECUGKPENWFGGFIGECUGU 
ō %QXGTCIGKUJKIJGTVJCPVTCFKVKQPCNVGUVU
6TCFKVKQPCNVGUVUNKOKVCVKQPU 
6GUVGF1- 
0QVVGUVGF$TGCMU 
2TQITCOQOCKP
6TCFKVKQPCNVGUVUNKOKVCVKQPU 
6GUVGF1- 
0QVVGUVGF$TGCMU 
2TQITCOQOCKP
5EJGFWNG 
 .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU 
 (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 
 6GUVECUGIGPGTCVKQP 
 GHKPKPICRTQITCORTQRGTVKGU 
 +PVTQFWEKPI#NIGDTCKEGUKIP 
 6JGECUGQH/QPQKF
5ECNC%JGEM 
ō #2TQRGTV[$CUGF6GUVKPIHTCOGYQTM 
ō #2+HQTCWVQOCVKEVGUVECUGIGPGTCVKQP 
ō #2+HQTRTQRGTV[FGHKPKVKQP 
ō 2TQRGTV[EJGEMKPITWPVKOGKPENWFKPI6GUV%CUG 
5KORNKHKECVKQP 
ō +PVGITCVKQPYKVJVGUVHTCOGYQTMU
UWEJCU5RGEUQT 
5ECNC6GUV
5ECNC%JGEMKUIQQFHQTŎ 
ō EJGEMKPIEQTTGEVPGUUQHCRTQITCO 
ō TGITGUUKQPEJGEMKPI 
ō RTQXKPICNIGDTCKE.CYU 
ō VGUVKPIYKVJNGUUEQFG 
ō KPXGUVKICVGRTQRGTV[HCKNWTGU
(KTUVGZCORNG 
val propMax = Prop.forAll { (x: Int, y: Int) = 
val z = math.max(x, y) 
(z == x || z == y)  (z = x  z = y) 
} 
propMax.check 
+ OK, passed 100 tests.
(KTUVGZCORNG 
7PKXGTUCN3WCPVKHKECVKQP∀ 
val propMax = Prop.forAll { (x: Int, y: Int) = 
val z = math.max(x, y) 
(z == x || z == y)  (z = x  z = y) 
} 
propMax.check 
+ OK, passed 100 tests.
(KTUVGZCORNG 
#DUVTCEVFGHKPKVKQPQH+PRWVU 
7PKXGTUCN3WCPVKHKECVKQP∀ 
val propMax = Prop.forAll { (x: Int, y: Int) = 
val z = math.max(x, y) 
(z == x || z == y)  (z = x  z = y) 
} 
propMax.check 
+ OK, passed 100 tests.
(KTUVGZCORNG 
#DUVTCEVFGHKPKVKQPQH+PRWVU 
val propMax = Prop.forAll { (x: Int, y: Int) = 
val z = math.max(x, y) 
(z == x || z == y)  (z = x  z = y) 
} 
propMax.check 
+ OK, passed 100 tests. 
%QORNGVGFGHKPKVKQPQHOCZ 
7PKXGTUCN3WCPVKHKECVKQP∀
(KTUVGZCORNG 
#DUVTCEVFGHKPKVKQPQH+PRWVU 
val propMax = Prop.forAll { (x: Int, y: Int) = 
val z = math.max(x, y) 
(z == x || z == y)  (z = x  z = y) 
} 
propMax.check 
+ OK, passed 100 tests. 
%QORNGVGFGHKPKVKQPQHOCZ 
7PKXGTUCN3WCPVKHKECVKQP∀ 
VGUVUJCXGDGGPTWP
5EJGFWNG 
 .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU 
 (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 
 6GUVECUGIGPGTCVKQP 
 GHKPKPICRTQITCORTQRGTVKGU 
 +PVTQFWEKPI#NIGDTCKEGUKIP 
 6JGECUGQH/QPQKF
6GUVECUGIGPGTCVKQP 
ō 5ECNC%JGEMRTQXKFGUIGPGTCVQTUVQFGHKPGCDUVTCEVVGUVECUGU 
ō 6JGUG IGPGTCVQTU CTG WUGF VQ TCPFQON[ ETGCVG EQPETGVG VGUV 
ECUGU 
ō )GP=6? FGHKPKVKQP KU SWKVG UKORNG # IGPGTCVQT GXGPVWCNN[ 
TGVWTPUCXCNWGQHV[RG6 
class Gen[+T] { 
def apply(prms: Gen.Params): Option[T] 
} 
%QPVCKPU4CPFQO)GPGTCVQT 
WUGFVQIGPGTCVGCTCPFQO6
7UKPICIGPGTCVQT 
)GP=6?TGVWTPUCP1RVKQP=6? 
Gen.choose(0, 100).sample 
res0: Option[Int] = Some(89) 
Prop.forAll(Gen.choose(0, 100)) ( _ = 100 ).check 
+ OK, passed 100 tests. 
)GPGTCVQTKURCUUGFGZRNKEKVN[
5ECNC%JGEM)GP 
5ECNC%JGEMFGHKPGUCNCTIGUGVQH)GPGTCVQTU 
scala Gen. 
alphaChar fail nonEmptyContainerOf sequence 
alphaLowerChar freqTuple nonEmptyListOf size 
alphaNumChar frequency nonEmptyMap sized 
alphaStr identifier numChar someOf 
alphaUpperChar isInstanceOf numStr toString 
asInstanceOf listOf oneOf uuid 
choose listOf1 option value 
chooseNum listOfN parameterized wrap 
const lzy pick zip 
containerOf mapOf posNum 
containerOf1 mapOfN resize 
containerOfN negNum resultOf
Custom Generators 
)GPKUC/QPCFCPFECPDGWUGFKPHQTEQORTGJGPUKQP 
val personGenerator = for { 
name - Gen.alphaStr 
age - Gen.choose(0, 150) 
} yield Person(name, age) 
Prop.forAll(personGenerator :| Person) { p = 
p.toString ?= sPerson(${p.name},${p.age}) 
} 
case class Person(name: String, age: Int)
1VJGTUCORNGUQH%WUVQO 
)GPGTCVQT 
#IGPGTCVQTECPFGRGPFHTQOXCNWGQHCPQVJGTQPG 
NKMGKPVJGHQNNQYKPIGZCORNG 
for { 
s - Arbitrary.arbitrary[String] 
index - Gen.choose(0, s.length) 
} yield (s, index)
9GKIJVGFIGPGTCVQTYKVJ 
HTGSWGPE[ 
val even = Gen.posNum[Int].suchThat(_ % 2 == 0) 
val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) 
val number = Gen.frequency((2, even), (4, odd), (1, 0)) 
Prop.forAll(number) { i = 
val label = 
if (i == 0) zero 
else if (i % 2 == 0) even 
else odd 
Prop.collect(label)(true) 
}.check(1000) 
+ OK, passed 1000 tests. 
 Collected test data: 
57% odd 
29% even 
14% zero
9GKIJVGFIGPGTCVQTYKVJ 
HTGSWGPE[ 
GHKPGYGKIJVHQTGCEJ)GPGTCVQT 
val even = Gen.posNum[Int].suchThat(_ % 2 == 0) 
val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) 
val number = Gen.frequency((2, even), (4, odd), (1, 0)) 
Prop.forAll(number) { i = 
val label = 
if (i == 0) zero 
else if (i % 2 == 0) even 
else odd 
Prop.collect(label)(true) 
}.check(1000) 
+ OK, passed 1000 tests. 
 Collected test data: 
57% odd 
29% even 
14% zero
9GKIJVGFIGPGTCVQTYKVJ 
HTGSWGPE[ 
GHKPGYGKIJVHQTGCEJ)GPGTCVQT 
val even = Gen.posNum[Int].suchThat(_ % 2 == 0) 
val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) 
val number = Gen.frequency((2, even), (4, odd), (1, 0)) 
Prop.forAll(number) { i = 
val label = 
if (i == 0) zero 
else if (i % 2 == 0) even 
else odd 
Prop.collect(label)(true) 
}.check(1000) 
+ OK, passed 1000 tests. 
 Collected test data: 
57% odd 
29% even 
14% zero 
GHKPGCNCDGNHQTGCEJ 
MKPFQHXCNWG
)GPGTCVGCNKUV 
sealed trait Coin 
case object Head extends Coin 
case object Tail extends Coin 
#TDKVTCT[UKGFNKUVQHCoin 
val coin = Gen.oneOf(Head, Tail) 
(KZGFUKGFNKUVQHCoin 
val coins = Gen.listOf(coin) 
val threeCoins = Gen.listOfN(3, coin) 
scala coins.sample 
res1: Option[List[Coin]] = Some(List(Head, Tail, Head, Tail, …)) 
scala threeCoins.sample 
res2: Option[List[Coin]] = Some(List(Head, Head, Tail))
5EJGFWNG 
 .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU 
 (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 
 6GUVECUGIGPGTCVKQP 
 GHKPKPICRTQITCORTQRGTVKGU 
 +PVTQFWEKPI#NIGDTCKEGUKIP 
 6JGECUGQH/QPQKF
2TQITCORTQRGTVKGU 
ō 5ECNC%JGEMIKXGUCDKNKV[VQFGHKPGVJGGZRGEVGF 
DGJCXKQTQHCRTQITCOKPCRTQITCOOCVKEYC[ 
ō 6JGRTQRGTV[QHCRTQITCOKUEQORQUGFQHVYQ 
VJKPIU 
C VJGFQOCKPQHVJGRTQITCO
HWPEVKQPWPFGTVGUV 
D CPCUUGTVKQPVJCVVJGRTQITCOOWUVXGTKH[ 
IKXGPRCTCOGVGTUHTQOVJGURGEKHKGFFQOCKP
4GOKPFGTFQOCKPEQFQOCKP 
QOCKPKUVJG+PRWV6[RGQHCHWPEVKQP%QQOCKPKU 
VJG1WVRWV6[RGQHCHWPEVKQP 
Int,String… HWPEVKQP Int, String… 
QOCKP %QQOCKP 
#QOCKPCPFC%QQOCKPECPDGGKVJGTRTKOKVKXG 
V[RGUVWRNGUNKUVUHWPEVKQPU#NIGDTCKECVC6[RGŎ
5CORNG2TQRGTV[ 
Prop.forAll { xs: List[Int] = xs.reverse.reverse == xs }
5CORNG2TQRGTV[ 
QOCKPKUCPCTDKVTCT[XCNWGQHV[RG.KUV=+PV? 
Prop.forAll { xs: List[Int] = xs.reverse.reverse == xs }
5CORNG2TQRGTV[ 
QOCKPKUCPCTDKVTCT[XCNWGQHV[RG.KUV=+PV? 
Prop.forAll { xs: List[Int] = xs.reverse.reverse == xs } 
6JKUKUCTQWPFVTKRRTQRGTV[ 
#RRNKECDNGQPN[HQTKPXGTUKDNGHWPEVKQPU
6GUVECUGTGUVTKEVKQP 
9KVJVGUVECUGTGUVTKEVKQPYGCFFEQPUVTCKPVQP 
RTQITCOFQOCKP
JGTGZKUCRQUKVKXGPWODGT 
import org.scalacheck.Prop.BooleanOperators 
Prop.forAll { x: Int = 
(x = 0) == { 
val root = math.sqrt(x) 
math.round(root * root) == x 
} 
}
6GUVECUGTGUVTKEVKQPUECWVKQP 
$GECTGHWNVQPQVDGVQQTGUVTKEVKXGKH[QWYCPV 
5ECNC%JGEMDGCDNGVQRTQFWEGXCNWGU 
val p = Prop.forAll { n: Int = 
(n = 0  n % 2 == 0  n % 3 == 0) == 
n = 0 
} 
scala p.check 
! Gave up after only 52 passed tests. 262 tests were discarded.
6GUVECUGTGUVTKEVKQPUECWVKQP 
$GECTGHWNVQPQVDGVQQTGUVTKEVKXGKH[QWYCPV 
5ECNC%JGEMDGCDNGVQRTQFWEGXCNWGU 
val p = Prop.forAll { n: Int = 
(n = 0  n % 2 == 0  n % 3 == 0) == 
n = 0 
} 
scala p.check 
! Gave up after only 52 passed tests. 262 tests were discarded. 
7UGKPUVGCFCEWUVQOIGPGTCVQT 
val genNum = Gen.choose(0, Int.MaxValue) 
.retryUntil(_ % 2 == 0) 
.retryUntil(_ % 3 == 0) 
val p = Prop.forAll(genNum) { x: Int = x = 0 } 
scala p.check 
+ OK, passed 100 tests.
6GUVECUGUKORNKHKECVKQP 
val p = Prop.forAll( 
Gen.choose(0, Int.MaxValue) :| x, 
Gen.choose(0, Int.MaxValue) :| y 
) { case (x, y) = 
add(x, y) = x  add(x, y) = y 
} 
scala p.check 
! Falsified after 0 passed tests. 
 x: 195667048 
 x_ORIGINAL: 1350546201 
 y: 1951816600 
 y_ORIGINAL: 2004457204
6GUVECUGUKORNKHKECVKQP 
val p = Prop.forAll( 
Gen.choose(0, Int.MaxValue) :| x, 
Gen.choose(0, Int.MaxValue) :| y 
) { case (x, y) = 
add(x, y) = x  add(x, y) = y 
} 
scala p.check 
! Falsified after 0 passed tests. 
 x: 195667048 
 x_ORIGINAL: 1350546201 
 y: 1951816600 
 y_ORIGINAL: 2004457204 
195667048 + 1951816600 == Int.MaxValue + 1
6GUVECUGUKORNKHKECVKQP 
val p = Prop.forAll( 
Gen.choose(0, Int.MaxValue) :| x, 
Gen.choose(0, Int.MaxValue) :| y 
) { case (x, y) = 
add(x, y) = x  add(x, y) = y 
} 
scala p.check 
! Falsified after 0 passed tests. 
 x: 195667048 
 x_ORIGINAL: 1350546201 
 y: 1951816600 
 y_ORIGINAL: 2004457204 
195667048 + 1951816600 == Int.MaxValue + 1 
VGUVECUGUKORNKHKECVKQPOGEJCPKUOJCUHQWPFVJG 
UKORNGUVVGUVECUGVJCVOCMGVJGVGUVHCKN
6GUVECUGUKORNKHKECVKQP 
val genListPosNum = Gen.listOf(Gen.choose(1, Int.MaxValue)) 
val p = Prop.forAll(genListPosNum :| positive numbers) { xs: List[Int] = 
xs.sorted.foldLeft(Option(0)) { 
case (mp, x) = mp.flatMap(p = if (p  x) Some(x) else None) 
}.isDefined 
} 
scala p.check 
! Falsified after 6 passed tests. 
 positive numbers: List(1, 1) 
 positive numbers_ORIGINAL: List(1901315974, 963110019, 991102462)
6GUVECUGUKORNKHKECVKQP 
val genListPosNum = Gen.listOf(Gen.choose(1, Int.MaxValue)) 
val p = Prop.forAll(genListPosNum :| positive numbers) { xs: List[Int] = 
xs.sorted.foldLeft(Option(0)) { 
case (mp, x) = mp.flatMap(p = if (p  x) Some(x) else None) 
}.isDefined 
} 
scala p.check 
! Falsified after 6 passed tests. 
 positive numbers: List(1, 1) 
 positive numbers_ORIGINAL: List(1901315974, 963110019, 991102462)
6GUVECUGUKORNKHKECVKQP 
val genListPosNum = Gen.listOf(Gen.choose(1, Int.MaxValue)) 
val p = Prop.forAll(genListPosNum :| positive numbers) { xs: List[Int] = 
xs.sorted.foldLeft(Option(0)) { 
case (mp, x) = mp.flatMap(p = if (p  x) Some(x) else None) 
}.isDefined 
} 
scala p.check 
! Falsified after 6 passed tests. 
 positive numbers: List(1, 1) 
 positive numbers_ORIGINAL: List(1901315974, 963110019, 991102462)
+PVGITCVKQPYKVJ5RGEU 
class SampleTest extends Specification with ScalaCheck { 
Integer should { 
have a max method in { 
Prop.forAll { (x: Int, y: Int) = 
val z = math.max(x, y) 
(z == x || z == y)  (z = x  z = y) 
} 
} 
}
5EJGFWNG 
 .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU 
 (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 
 6GUVECUGIGPGTCVKQP 
 GHKPKPICRTQITCORTQRGTVKGU 
 +PVTQFWEKPI#NIGDTCKEGUKIP 
 6JGECUGQH/QPQKF
#NIGDTCKEFGUKIP 
ō #NIGDTCKEFGUKIPKUWUGFVQFGHKPGNKDTCTKGUQH 
HWPEVKQPU 
ō +PCNIGDTCKEFGUKIPYGHQEWUQPHWPEVKQPU 
QRGTCVKPIQXGTFCVCV[RGUCPFNCYUVJCVFGHKPG 
VJGUGHWPEVKQPU 
ō 6JGCNIGDTCQHCNKDTCT[FQGUPņVFGRGPFQPVJG 
KPVGTPCNUQHVJGFCVCV[RGUKVQRGTCVGUQXGT
#NIGDTCKEFGUKIP 
ō 5GXGTCNV[RGENCUUGUGZKUVUYKVJCRTQRGTN[FGHKPGF 
CNIGDTC
HWPEVKQPUCPFNCYU 
ō /QUVRQRWNCTV[RGENCUUGUKPENWFG/QPCFU 
/QPQKFU(WPEVQT#RRNKECVKXGUŎ 
ō 6JGHWPEVKQPCNRTQITCOOKPINKDTCT[5ECNCFGHKPGU 
CNCTIGUGVQHV[RGENCUUGUYKVJVJGKTEQODKPCVQTU 
CPF.CYFGHKPKVKQPU
5EJGFWNG 
 .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU 
 (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 
 6GUVECUGIGPGTCVKQP 
 GHKPKPICRTQITCORTQRGTVKGU 
 +PVTQFWEKPI#NIGDTCKEGUKIP 
 6JGECUGQH/QPQKF
6JGECUGQH/QPQKF 
ō #6[RG#KUC/QPQKFKHVJGTGKU 
ō #PCUUQEKCVKXGENQUGFDKPCT[QRGTCVKQP 
ō #PKFGPVKV[GNGOGPV
QTGTQ 
ō #UCPGZCORNG+PVKUC/QPQKFYKVJ
CUDKPCT[ 
QRGTCVKQPCPFCUKFGPVKV[GNGOGPV
GHKPKVKQPQHC/QPQKF 
trait Monoid[A] { 
def op(a1: A, a2: A): A 
def zero: A 
object Laws { 
import scalaio.scalacheck.Eq 
import Eq._ 
def monoidIsAssociative(in: Gen[(A, A, A)])(implicit eq: Eq[A]): Prop = 
Prop.forAll(in) { case (x, y, z) = 
op(op(x, y), z) === op(x, op(y, z)) 
} 
def monoidHaveZero(in: Gen[A])(implicit eq: Eq[A]): Prop = 
Prop.forAll(in) { a = (op(a, zero) === a)  (op(zero, a) === a)} 
} 
}
'ZCORNGUQH/QPQKF 
val stringMonoid = new Monoid[String] { 
override def zero =  
override def op(a1: String, a2: String) = a1 + a2 
} 
def listMonoid[A] = new Monoid[List[A]] { 
override def op(a1: List[A], a2: List[A]) = a1 ++ a2 
override def zero = List.empty[A] 
} 
val intAdditionMonoid = new Monoid[Int] { 
override def op(a1: Int, a2: Int) = a1 + a2 
override def zero = 0 
} 
val intMultiplicationMonoid = new Monoid[Int] { 
override def op(a1: Int, a2: Int) = a1 * a2 
override def zero = 1 
} 
def endoMonoid[A] = new Monoid[A = A] { 
override def op(a1: A = A, a2: A = A) = { a: A = a1(a2(a)) } 
override def zero = identity 
}
%QODKPCVQTUHQT/QPQKFU 
def productMonoid[A, B](a: Monoid[A], b: Monoid[B]): Monoid[(A, B)] = new Monoid[(A, B)] { 
override def op(a1: (A, B), a2: (A, B)) = (a.op(a1._1, a2._1), b.op(a1._2, a2._2)) 
override def zero = (a.zero, b.zero) 
} 
def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] = new Monoid[Map[K, V]] { 
override def op(a1: Map[K, V], a2: Map[K, V]) = a1.map { 
case (k, v) = (k, V.op(v, a2.get(k) getOrElse V.zero)) 
} 
override def zero = Map() 
} 
9GECPETGCVGPGY/QPQKFUHTQOGZKUVKPIKPUVCPEGU
%QODKPCVQTUHQT/QPQKFU 
)KXGP/QPQKFCUUQEKCVKXKV[NCY/QPQKFKPUVCPEGUCTG 
RTGVV[YGNNUWKVGFVQRGTHQTOKPRCTCNNGNQTKP 
FKUVTKDWVGFYC[ 
def parReduce[A](as: List[A]) 
(implicit M: Monoid[A]): A = ??? 
def parMapReduce[A, B](as: List[A], f: A = B) 
(implicit M: Monoid[B]): B = ???
6GUVKPI/QPQKF.CYU 
String Monoid should { 
be associative in { 
val genTriple = arbitrary[(String, String, String)] 
Monoids.stringMonoid.Laws.monoidIsAssociative(genTriple) 
} 
have a zero in { 
Monoids.stringMonoid.Laws.monoidHaveZero(arbitrary[String]) 
} 
}
'SV[RGENCUUFGHKPKVKQP 
trait Eq[A] { 
def equals(a1: A, a2: A): Prop 
} 
object Eq { 
implicit def orderedEq[A: Ordering]: Eq[A] = new Eq[A] { 
override def equals(a1: A, a2: A) = implicitly[Ordering[A]].equiv(a1, a2) 
} 
implicit def functionEq[A: Arbitrary, B]: Eq[A = B] = new Eq[A = B] { 
override def equals(f1: (A) = B, f2: (A) = B) = 
Prop.forAll(arbitrary[A]) { a = f1(a) == f2(a) } 
} 
implicit def listEq[A]: Eq[List[A]] = new Eq[List[A]] { 
override def equals(a1: List[A], a2: List[A]) = a1 == a2 
} 
implicit class EqOps[A: Eq](a1: A) { 
def ===(a2: A): Prop = implicitly[Eq[A]].equals(a1, a2) 
} 
}
6QIQHWTVJGT 
Functional Programming in Scala 
Paul Chiusano and Rúnar Bjarnason 
Manning 
ScalaCheck 
The Definitive Guide 
Richard Nilson 
artima
3WGUVKQPU!

Property Based Testing with ScalaCheck

  • 1.
    2TQRGTV[$CUGF6GUVKPI YKVJ5ECNC%JGEM CXKF)CNKEJGV (TGGNCPEG(WPEVKQPCN2TQITCOOGT 6YKVVGTFICNKEJGV
  • 2.
    5EJGFWNG .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 6GUVECUGIGPGTCVKQP GHKPKPICRTQITCORTQRGTVKGU +PVTQFWEKPI#NIGDTCKEGUKIP 6JGECUGQH/QPQKF
  • 3.
    1RVKQPUVQEJGEMEQTTGEVPGUUQH CRTQITCO ō6GUVTKXGPGXGNQROGPVVGEJPKSWGU Z7PKVŎ ō $GJCXKQTTKXGPGXGNQROGPV ō 2TQRGTV[$CUGF6GUVKPI ō 6[RG TKXGP GXGNQROGPV GRGPFGPV 6[RGU 6JGQTGO 2TQXGTU 2CTCOGVTKEKV[ 6JGQTGOU HQT (TGGŎ
  • 4.
    6GUVTKXGPGXGNQROGPV ō #RTQITCOKUXGTKHKGFD[UGXGTCNWPKVVGUVU ō 2TQITCOCPFVGUVUCTGFGXGNQRGFKVGTCVKXGN[ ō 6GUVU CTG EQPETGVG GZCORNGU QH JQY C RTQITCO UJQWNFRGTHQTOKPCRCTVKEWNCTUKVWCVKQP ō 6JG[FQPņVIKXGCUUWTCPEGVJCVVJGQXGTCNNDGJCXKQT QHCRTQITCOKUEQXGTGF
  • 5.
    $GJCXKQTTKXGPGXGNQROGPV ō 5RGEKHKECVKQPUCTG FGHKPGF WUKPI C PCVWTCN HQTOCNKUO VJCV KU GZGEWVCDNG CPF ECP XGTKH[ C RTQITCO ō 6GUVU OWUV DG GZJCWUVKXG VQ EQXGT VJG QXGTCNN RTQITCODGJCXKQT
  • 6.
    2TQRGTV[$CUGF6GUVKPI ō 2TQRGTV[$CUGF6GUVKPICNNQY[QWVQFGUETKDG[QWT RTQITCO RTQRGTVKGU NGVVKPI VJG WPFGTN[KPI HTCOGYQTM IGPGTCVG VGUV ECUGU CPF XGTKH[ RTQRGTVKGU ō #RTQRGTV[KUCPCDUVTCEVURGEKHKECVKQP ō )GPGTCVGFVGUVECUGKPENWFGGFIGECUGU ō %QXGTCIGKUJKIJGTVJCPVTCFKVKQPCNVGUVU
  • 7.
  • 8.
  • 9.
    5EJGFWNG .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 6GUVECUGIGPGTCVKQP GHKPKPICRTQITCORTQRGTVKGU +PVTQFWEKPI#NIGDTCKEGUKIP 6JGECUGQH/QPQKF
  • 10.
    5ECNC%JGEM ō #2TQRGTV[$CUGF6GUVKPIHTCOGYQTM ō #2+HQTCWVQOCVKEVGUVECUGIGPGTCVKQP ō #2+HQTRTQRGTV[FGHKPKVKQP ō 2TQRGTV[EJGEMKPITWPVKOGKPENWFKPI6GUV%CUG 5KORNKHKECVKQP ō +PVGITCVKQPYKVJVGUVHTCOGYQTMU UWEJCU5RGEUQT 5ECNC6GUV
  • 11.
    5ECNC%JGEMKUIQQFHQTŎ ō EJGEMKPIEQTTGEVPGUUQHCRTQITCO ō TGITGUUKQPEJGEMKPI ō RTQXKPICNIGDTCKE.CYU ō VGUVKPIYKVJNGUUEQFG ō KPXGUVKICVGRTQRGTV[HCKNWTGU
  • 12.
    (KTUVGZCORNG val propMax= Prop.forAll { (x: Int, y: Int) = val z = math.max(x, y) (z == x || z == y) (z = x z = y) } propMax.check + OK, passed 100 tests.
  • 13.
    (KTUVGZCORNG 7PKXGTUCN3WCPVKHKECVKQP∀ valpropMax = Prop.forAll { (x: Int, y: Int) = val z = math.max(x, y) (z == x || z == y) (z = x z = y) } propMax.check + OK, passed 100 tests.
  • 14.
    (KTUVGZCORNG #DUVTCEVFGHKPKVKQPQH+PRWVU 7PKXGTUCN3WCPVKHKECVKQP∀ val propMax = Prop.forAll { (x: Int, y: Int) = val z = math.max(x, y) (z == x || z == y) (z = x z = y) } propMax.check + OK, passed 100 tests.
  • 15.
    (KTUVGZCORNG #DUVTCEVFGHKPKVKQPQH+PRWVU valpropMax = Prop.forAll { (x: Int, y: Int) = val z = math.max(x, y) (z == x || z == y) (z = x z = y) } propMax.check + OK, passed 100 tests. %QORNGVGFGHKPKVKQPQHOCZ 7PKXGTUCN3WCPVKHKECVKQP∀
  • 16.
    (KTUVGZCORNG #DUVTCEVFGHKPKVKQPQH+PRWVU valpropMax = Prop.forAll { (x: Int, y: Int) = val z = math.max(x, y) (z == x || z == y) (z = x z = y) } propMax.check + OK, passed 100 tests. %QORNGVGFGHKPKVKQPQHOCZ 7PKXGTUCN3WCPVKHKECVKQP∀ VGUVUJCXGDGGPTWP
  • 17.
    5EJGFWNG .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 6GUVECUGIGPGTCVKQP GHKPKPICRTQITCORTQRGTVKGU +PVTQFWEKPI#NIGDTCKEGUKIP 6JGECUGQH/QPQKF
  • 18.
    6GUVECUGIGPGTCVKQP ō 5ECNC%JGEMRTQXKFGUIGPGTCVQTUVQFGHKPGCDUVTCEVVGUVECUGU ō 6JGUG IGPGTCVQTU CTG WUGF VQ TCPFQON[ ETGCVG EQPETGVG VGUV ECUGU ō )GP=6? FGHKPKVKQP KU SWKVG UKORNG # IGPGTCVQT GXGPVWCNN[ TGVWTPUCXCNWGQHV[RG6 class Gen[+T] { def apply(prms: Gen.Params): Option[T] } %QPVCKPU4CPFQO)GPGTCVQT WUGFVQIGPGTCVGCTCPFQO6
  • 19.
    7UKPICIGPGTCVQT )GP=6?TGVWTPUCP1RVKQP=6? Gen.choose(0,100).sample res0: Option[Int] = Some(89) Prop.forAll(Gen.choose(0, 100)) ( _ = 100 ).check + OK, passed 100 tests. )GPGTCVQTKURCUUGFGZRNKEKVN[
  • 20.
    5ECNC%JGEM)GP 5ECNC%JGEMFGHKPGUCNCTIGUGVQH)GPGTCVQTU scalaGen. alphaChar fail nonEmptyContainerOf sequence alphaLowerChar freqTuple nonEmptyListOf size alphaNumChar frequency nonEmptyMap sized alphaStr identifier numChar someOf alphaUpperChar isInstanceOf numStr toString asInstanceOf listOf oneOf uuid choose listOf1 option value chooseNum listOfN parameterized wrap const lzy pick zip containerOf mapOf posNum containerOf1 mapOfN resize containerOfN negNum resultOf
  • 21.
    Custom Generators )GPKUC/QPCFCPFECPDGWUGFKPHQTEQORTGJGPUKQP val personGenerator = for { name - Gen.alphaStr age - Gen.choose(0, 150) } yield Person(name, age) Prop.forAll(personGenerator :| Person) { p = p.toString ?= sPerson(${p.name},${p.age}) } case class Person(name: String, age: Int)
  • 22.
    1VJGTUCORNGUQH%WUVQO )GPGTCVQT #IGPGTCVQTECPFGRGPFHTQOXCNWGQHCPQVJGTQPG NKMGKPVJGHQNNQYKPIGZCORNG for { s - Arbitrary.arbitrary[String] index - Gen.choose(0, s.length) } yield (s, index)
  • 23.
    9GKIJVGFIGPGTCVQTYKVJ HTGSWGPE[ valeven = Gen.posNum[Int].suchThat(_ % 2 == 0) val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) val number = Gen.frequency((2, even), (4, odd), (1, 0)) Prop.forAll(number) { i = val label = if (i == 0) zero else if (i % 2 == 0) even else odd Prop.collect(label)(true) }.check(1000) + OK, passed 1000 tests. Collected test data: 57% odd 29% even 14% zero
  • 24.
    9GKIJVGFIGPGTCVQTYKVJ HTGSWGPE[ GHKPGYGKIJVHQTGCEJ)GPGTCVQT val even = Gen.posNum[Int].suchThat(_ % 2 == 0) val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) val number = Gen.frequency((2, even), (4, odd), (1, 0)) Prop.forAll(number) { i = val label = if (i == 0) zero else if (i % 2 == 0) even else odd Prop.collect(label)(true) }.check(1000) + OK, passed 1000 tests. Collected test data: 57% odd 29% even 14% zero
  • 25.
    9GKIJVGFIGPGTCVQTYKVJ HTGSWGPE[ GHKPGYGKIJVHQTGCEJ)GPGTCVQT val even = Gen.posNum[Int].suchThat(_ % 2 == 0) val odd = Gen.posNum[Int].suchThat(_ % 2 == 1) val number = Gen.frequency((2, even), (4, odd), (1, 0)) Prop.forAll(number) { i = val label = if (i == 0) zero else if (i % 2 == 0) even else odd Prop.collect(label)(true) }.check(1000) + OK, passed 1000 tests. Collected test data: 57% odd 29% even 14% zero GHKPGCNCDGNHQTGCEJ MKPFQHXCNWG
  • 26.
    )GPGTCVGCNKUV sealed traitCoin case object Head extends Coin case object Tail extends Coin #TDKVTCT[UKGFNKUVQHCoin val coin = Gen.oneOf(Head, Tail) (KZGFUKGFNKUVQHCoin val coins = Gen.listOf(coin) val threeCoins = Gen.listOfN(3, coin) scala coins.sample res1: Option[List[Coin]] = Some(List(Head, Tail, Head, Tail, …)) scala threeCoins.sample res2: Option[List[Coin]] = Some(List(Head, Head, Tail))
  • 27.
    5EJGFWNG .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 6GUVECUGIGPGTCVKQP GHKPKPICRTQITCORTQRGTVKGU +PVTQFWEKPI#NIGDTCKEGUKIP 6JGECUGQH/QPQKF
  • 28.
    2TQITCORTQRGTVKGU ō 5ECNC%JGEMIKXGUCDKNKV[VQFGHKPGVJGGZRGEVGF DGJCXKQTQHCRTQITCOKPCRTQITCOOCVKEYC[ ō 6JGRTQRGTV[QHCRTQITCOKUEQORQUGFQHVYQ VJKPIU C VJGFQOCKPQHVJGRTQITCO HWPEVKQPWPFGTVGUV D CPCUUGTVKQPVJCVVJGRTQITCOOWUVXGTKH[ IKXGPRCTCOGVGTUHTQOVJGURGEKHKGFFQOCKP
  • 29.
    4GOKPFGTFQOCKPEQFQOCKP QOCKPKUVJG+PRWV6[RGQHCHWPEVKQP%QQOCKPKU VJG1WVRWV6[RGQHCHWPEVKQP Int,String… HWPEVKQP Int, String… QOCKP %QQOCKP #QOCKPCPFC%QQOCKPECPDGGKVJGTRTKOKVKXG V[RGUVWRNGUNKUVUHWPEVKQPU#NIGDTCKECVC6[RGŎ
  • 30.
    5CORNG2TQRGTV[ Prop.forAll {xs: List[Int] = xs.reverse.reverse == xs }
  • 31.
    5CORNG2TQRGTV[ QOCKPKUCPCTDKVTCT[XCNWGQHV[RG.KUV=+PV? Prop.forAll{ xs: List[Int] = xs.reverse.reverse == xs }
  • 32.
    5CORNG2TQRGTV[ QOCKPKUCPCTDKVTCT[XCNWGQHV[RG.KUV=+PV? Prop.forAll{ xs: List[Int] = xs.reverse.reverse == xs } 6JKUKUCTQWPFVTKRRTQRGTV[ #RRNKECDNGQPN[HQTKPXGTUKDNGHWPEVKQPU
  • 33.
    6GUVECUGTGUVTKEVKQP 9KVJVGUVECUGTGUVTKEVKQPYGCFFEQPUVTCKPVQP RTQITCOFQOCKP JGTGZKUCRQUKVKXGPWODGT import org.scalacheck.Prop.BooleanOperators Prop.forAll { x: Int = (x = 0) == { val root = math.sqrt(x) math.round(root * root) == x } }
  • 34.
    6GUVECUGTGUVTKEVKQPUECWVKQP $GECTGHWNVQPQVDGVQQTGUVTKEVKXGKH[QWYCPV 5ECNC%JGEMDGCDNGVQRTQFWEGXCNWGU val p = Prop.forAll { n: Int = (n = 0 n % 2 == 0 n % 3 == 0) == n = 0 } scala p.check ! Gave up after only 52 passed tests. 262 tests were discarded.
  • 35.
    6GUVECUGTGUVTKEVKQPUECWVKQP $GECTGHWNVQPQVDGVQQTGUVTKEVKXGKH[QWYCPV 5ECNC%JGEMDGCDNGVQRTQFWEGXCNWGU val p = Prop.forAll { n: Int = (n = 0 n % 2 == 0 n % 3 == 0) == n = 0 } scala p.check ! Gave up after only 52 passed tests. 262 tests were discarded. 7UGKPUVGCFCEWUVQOIGPGTCVQT val genNum = Gen.choose(0, Int.MaxValue) .retryUntil(_ % 2 == 0) .retryUntil(_ % 3 == 0) val p = Prop.forAll(genNum) { x: Int = x = 0 } scala p.check + OK, passed 100 tests.
  • 36.
    6GUVECUGUKORNKHKECVKQP val p= Prop.forAll( Gen.choose(0, Int.MaxValue) :| x, Gen.choose(0, Int.MaxValue) :| y ) { case (x, y) = add(x, y) = x add(x, y) = y } scala p.check ! Falsified after 0 passed tests. x: 195667048 x_ORIGINAL: 1350546201 y: 1951816600 y_ORIGINAL: 2004457204
  • 37.
    6GUVECUGUKORNKHKECVKQP val p= Prop.forAll( Gen.choose(0, Int.MaxValue) :| x, Gen.choose(0, Int.MaxValue) :| y ) { case (x, y) = add(x, y) = x add(x, y) = y } scala p.check ! Falsified after 0 passed tests. x: 195667048 x_ORIGINAL: 1350546201 y: 1951816600 y_ORIGINAL: 2004457204 195667048 + 1951816600 == Int.MaxValue + 1
  • 38.
    6GUVECUGUKORNKHKECVKQP val p= Prop.forAll( Gen.choose(0, Int.MaxValue) :| x, Gen.choose(0, Int.MaxValue) :| y ) { case (x, y) = add(x, y) = x add(x, y) = y } scala p.check ! Falsified after 0 passed tests. x: 195667048 x_ORIGINAL: 1350546201 y: 1951816600 y_ORIGINAL: 2004457204 195667048 + 1951816600 == Int.MaxValue + 1 VGUVECUGUKORNKHKECVKQPOGEJCPKUOJCUHQWPFVJG UKORNGUVVGUVECUGVJCVOCMGVJGVGUVHCKN
  • 39.
    6GUVECUGUKORNKHKECVKQP val genListPosNum= Gen.listOf(Gen.choose(1, Int.MaxValue)) val p = Prop.forAll(genListPosNum :| positive numbers) { xs: List[Int] = xs.sorted.foldLeft(Option(0)) { case (mp, x) = mp.flatMap(p = if (p x) Some(x) else None) }.isDefined } scala p.check ! Falsified after 6 passed tests. positive numbers: List(1, 1) positive numbers_ORIGINAL: List(1901315974, 963110019, 991102462)
  • 40.
    6GUVECUGUKORNKHKECVKQP val genListPosNum= Gen.listOf(Gen.choose(1, Int.MaxValue)) val p = Prop.forAll(genListPosNum :| positive numbers) { xs: List[Int] = xs.sorted.foldLeft(Option(0)) { case (mp, x) = mp.flatMap(p = if (p x) Some(x) else None) }.isDefined } scala p.check ! Falsified after 6 passed tests. positive numbers: List(1, 1) positive numbers_ORIGINAL: List(1901315974, 963110019, 991102462)
  • 41.
    6GUVECUGUKORNKHKECVKQP val genListPosNum= Gen.listOf(Gen.choose(1, Int.MaxValue)) val p = Prop.forAll(genListPosNum :| positive numbers) { xs: List[Int] = xs.sorted.foldLeft(Option(0)) { case (mp, x) = mp.flatMap(p = if (p x) Some(x) else None) }.isDefined } scala p.check ! Falsified after 6 passed tests. positive numbers: List(1, 1) positive numbers_ORIGINAL: List(1901315974, 963110019, 991102462)
  • 42.
    +PVGITCVKQPYKVJ5RGEU class SampleTestextends Specification with ScalaCheck { Integer should { have a max method in { Prop.forAll { (x: Int, y: Int) = val z = math.max(x, y) (z == x || z == y) (z = x z = y) } } }
  • 43.
    5EJGFWNG .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 6GUVECUGIGPGTCVKQP GHKPKPICRTQITCORTQRGTVKGU +PVTQFWEKPI#NIGDTCKEGUKIP 6JGECUGQH/QPQKF
  • 44.
    #NIGDTCKEFGUKIP ō #NIGDTCKEFGUKIPKUWUGFVQFGHKPGNKDTCTKGUQH HWPEVKQPU ō +PCNIGDTCKEFGUKIPYGHQEWUQPHWPEVKQPU QRGTCVKPIQXGTFCVCV[RGUCPFNCYUVJCVFGHKPG VJGUGHWPEVKQPU ō 6JGCNIGDTCQHCNKDTCT[FQGUPņVFGRGPFQPVJG KPVGTPCNUQHVJGFCVCV[RGUKVQRGTCVGUQXGT
  • 45.
    #NIGDTCKEFGUKIP ō 5GXGTCNV[RGENCUUGUGZKUVUYKVJCRTQRGTN[FGHKPGF CNIGDTC HWPEVKQPUCPFNCYU ō /QUVRQRWNCTV[RGENCUUGUKPENWFG/QPCFU /QPQKFU(WPEVQT#RRNKECVKXGUŎ ō 6JGHWPEVKQPCNRTQITCOOKPINKDTCT[5ECNCFGHKPGU CNCTIGUGVQHV[RGENCUUGUYKVJVJGKTEQODKPCVQTU CPF.CYFGHKPKVKQPU
  • 46.
    5EJGFWNG .KOKVCVKQPUQHVTCFKVKQPCNVGUVKPIVGEJPKSWGU (KTUVGZCORNGQH2TQRGTV[$CUGF6GUVKPIYKVJ5ECNC%JGEM 6GUVECUGIGPGTCVKQP GHKPKPICRTQITCORTQRGTVKGU +PVTQFWEKPI#NIGDTCKEGUKIP 6JGECUGQH/QPQKF
  • 47.
    6JGECUGQH/QPQKF ō #6[RG#KUC/QPQKFKHVJGTGKU ō #PCUUQEKCVKXGENQUGFDKPCT[QRGTCVKQP ō #PKFGPVKV[GNGOGPV QTGTQ ō #UCPGZCORNG+PVKUC/QPQKFYKVJ CUDKPCT[ QRGTCVKQPCPFCUKFGPVKV[GNGOGPV
  • 48.
    GHKPKVKQPQHC/QPQKF trait Monoid[A]{ def op(a1: A, a2: A): A def zero: A object Laws { import scalaio.scalacheck.Eq import Eq._ def monoidIsAssociative(in: Gen[(A, A, A)])(implicit eq: Eq[A]): Prop = Prop.forAll(in) { case (x, y, z) = op(op(x, y), z) === op(x, op(y, z)) } def monoidHaveZero(in: Gen[A])(implicit eq: Eq[A]): Prop = Prop.forAll(in) { a = (op(a, zero) === a) (op(zero, a) === a)} } }
  • 49.
    'ZCORNGUQH/QPQKF val stringMonoid= new Monoid[String] { override def zero = override def op(a1: String, a2: String) = a1 + a2 } def listMonoid[A] = new Monoid[List[A]] { override def op(a1: List[A], a2: List[A]) = a1 ++ a2 override def zero = List.empty[A] } val intAdditionMonoid = new Monoid[Int] { override def op(a1: Int, a2: Int) = a1 + a2 override def zero = 0 } val intMultiplicationMonoid = new Monoid[Int] { override def op(a1: Int, a2: Int) = a1 * a2 override def zero = 1 } def endoMonoid[A] = new Monoid[A = A] { override def op(a1: A = A, a2: A = A) = { a: A = a1(a2(a)) } override def zero = identity }
  • 50.
    %QODKPCVQTUHQT/QPQKFU def productMonoid[A,B](a: Monoid[A], b: Monoid[B]): Monoid[(A, B)] = new Monoid[(A, B)] { override def op(a1: (A, B), a2: (A, B)) = (a.op(a1._1, a2._1), b.op(a1._2, a2._2)) override def zero = (a.zero, b.zero) } def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] = new Monoid[Map[K, V]] { override def op(a1: Map[K, V], a2: Map[K, V]) = a1.map { case (k, v) = (k, V.op(v, a2.get(k) getOrElse V.zero)) } override def zero = Map() } 9GECPETGCVGPGY/QPQKFUHTQOGZKUVKPIKPUVCPEGU
  • 51.
    %QODKPCVQTUHQT/QPQKFU )KXGP/QPQKFCUUQEKCVKXKV[NCY/QPQKFKPUVCPEGUCTG RTGVV[YGNNUWKVGFVQRGTHQTOKPRCTCNNGNQTKP FKUVTKDWVGFYC[ def parReduce[A](as: List[A]) (implicit M: Monoid[A]): A = ??? def parMapReduce[A, B](as: List[A], f: A = B) (implicit M: Monoid[B]): B = ???
  • 52.
    6GUVKPI/QPQKF.CYU String Monoidshould { be associative in { val genTriple = arbitrary[(String, String, String)] Monoids.stringMonoid.Laws.monoidIsAssociative(genTriple) } have a zero in { Monoids.stringMonoid.Laws.monoidHaveZero(arbitrary[String]) } }
  • 53.
    'SV[RGENCUUFGHKPKVKQP trait Eq[A]{ def equals(a1: A, a2: A): Prop } object Eq { implicit def orderedEq[A: Ordering]: Eq[A] = new Eq[A] { override def equals(a1: A, a2: A) = implicitly[Ordering[A]].equiv(a1, a2) } implicit def functionEq[A: Arbitrary, B]: Eq[A = B] = new Eq[A = B] { override def equals(f1: (A) = B, f2: (A) = B) = Prop.forAll(arbitrary[A]) { a = f1(a) == f2(a) } } implicit def listEq[A]: Eq[List[A]] = new Eq[List[A]] { override def equals(a1: List[A], a2: List[A]) = a1 == a2 } implicit class EqOps[A: Eq](a1: A) { def ===(a2: A): Prop = implicitly[Eq[A]].equals(a1, a2) } }
  • 54.
    6QIQHWTVJGT Functional Programmingin Scala Paul Chiusano and Rúnar Bjarnason Manning ScalaCheck The Definitive Guide Richard Nilson artima
  • 55.