fuzyco
January 11, 2018
460

# Try Cats

Scalaの関数型ライブラリCatsをやってみた話です。

January 11, 2018

## Transcript

1. 5SZ\$BUT

2. ΞδΣϯμ
w \$BUTͱ͸
w \$BUTಠࣗͷϞφυ
w \$BUTΛ௨ֶͯ͠Μͩ͜ͱ

3. \$BUTͱ͸
4DBMB[ͷ಺෦෼྾ʹΑͬͯੜ·Εͨ
ؔ਺ܕϓϩάϥϛϯά༻ͷ4DBMBϥΠϒϥϦ
ɾ೥ࠒ஀ੜ
ɾʹWϦϦʔε
ɾݍ DBUFHPSZ
ʹ༝དྷ
ಡΜͩจݙ
ɾೣ൪IUUQFFETJODPNIFSEJOHDBUTKB
ɾ4DBMB8JUI\$BUT

4. \$BUTͷಛ௃
ɾؔ਺ܕϓϩάϥϛϯάΛ࣮ݱ͢ΔͨΊͷ
๛෋ͳܕΫϥεͷఏڙ
ɾIBTLFMMʹ͍࣮ۙ૷ &Rɺ4IPXͳͲ

5. \$BUTWT4DBMB[
\$BUT
ɾ৽͍͠σʔλܕ
ɾ࣮༻্ඞཁͳશͯͷ൚༻ؔ਺ͷ࣮૷
ΞυϗοΫଟ૬ੑɺUSBJUJNQMJDJU

ೣ൪ΑΓҾ༻
4DBMB[
ɾ৽͍͠σʔλܕ
ɾඪ४Ϋϥεͷ֦ு 0QUJPO0QT -JTU0QT

ɾ࣮༻্ඞཁͳશͯͷ൚༻ؔ਺ͷ࣮૷
ΞυϗοΫଟ૬ੑɺUSBJUJNQMJDJU

ಠश4DBMB[ΑΓҾ༻

6. \$BUTWT4DBMB[
&JUIFSa
7BMJEBUFE7BMJEBUJPO
3FBEFS3FBEFS
4UBUF4UBUF
8SJUFS8SJUFS
ɾ༻ҙ͞Ε͍ͯΔϝιου͕Ұ෦ҟͳΔ͕ɺ
ಉ͡Α͏ͳσʔλܕ͕͋Δ

7. \$BUTಠࣗͷϞφυ
ɾ&WBM

8. &WBMϞφυ
ධՁΛ੍ޚ͢Δσʔλܕ
import cats.Eval
// valͷΑ͏ʹఆٛ࣌ʹධՁ
val x = Eval.now {
println("Computing X")
math.random
}
// Computing X
// x: cats.Eval[Double]
= Now(0.8724950064732552)
// defͷΑ͏ʹৗʹධՁ
val y = Eval.always {
println("Computing Y")
math.random
}
// y: cats.Eval[Double]
= [email protected]
// lazy valͷΑ͏ʹ஗ԆධՁ
val z = Eval.later {
println("Computing Z")
math.random
}
// z: cats.Eval[Double]
= [email protected]

9. &WBMϞφυ
ධՁΛ੍ޚ͢Δσʔλܕ
x.value // first access
// res9: Double = 0.8724950064732552
x.value // second access
// res10: Double = 0.8724950064732552
y.value // first access
// Computing Y
// res11: Double = 0.8795680260041828
y.value // second access
// Computing Y
// res12: Double = 0.5640213059400854
z.value // first access
// Computing Z
// res13: Double = 0.5813583535421343
z.value // second access
// res14: Double = 0.5813583535421343
import cats.Eval
// valͷΑ͏ʹఆٛ࣌ʹධՁ
val x = Eval.now {
println("Computing X")
math.random
}
// Computing X
// x: cats.Eval[Double]
= Now(0.8724950064732552)
// defͷΑ͏ʹৗʹධՁ
val y = Eval.always {
println("Computing Y")
math.random
}
// y: cats.Eval[Double]
= [email protected]
// lazy valͷΑ͏ʹ஗ԆධՁ
val z = Eval.later {
println("Computing Z")
math.random
}
// z: cats.Eval[Double]
= [email protected]

10. &WBMʹΑΔ࠶ؼॲཧ
def factorial(n: BigInt): BigInt =
if(n == 1) n else n * factorial(n - 1)
factorial(50000) // ࣮ߦ
຤ඌ࠶ؼͰͳ͍࠶ؼؔ਺

11. &WBMʹΑΔ࠶ؼॲཧ
def factorial(n: BigInt): BigInt =
if(n == 1) n else n * factorial(n - 1)
factorial(50000) // ࣮ߦ
຤ඌ࠶ؼͰͳ͍࠶ؼؔ਺
ελοΫΦʔόʔϑϩʔ͕ى͖Δ
factorial(50000)
// java.lang.StackOverflowError
// ...

12. &WBMʹΑΔ࠶ؼॲཧ
def factorial(n: BigInt): Eval[BigInt] =
if(n == 1) {
Eval.now(n)
} else {
factorial(n - 1).map(_ * n)
}
factorial(50000).value // ࣮ߦ
&WBMΛ༻͍ͨίʔυʹมߋ

13. &WBMʹΑΔ࠶ؼॲཧ
def factorial(n: BigInt): Eval[BigInt] =
if(n == 1) {
Eval.now(n)
} else {
factorial(n - 1).map(_ * n)
}
factorial(50000).value // ࣮ߦ
&WBMΛ༻͍ͨίʔυʹมߋ
factorial(50000).value
// java.lang.StackOverflowError
// ...
ελοΫΦʔόʔϑϩʔ͕ى͖Δ

14. &WBMʹΑΔ࠶ؼॲཧ
def factorial(n: BigInt): Eval[BigInt] =
if(n == 1) {
Eval.now(n)
} else {
Eval.defer(factorial(n - 1).map(_ * n))
}
factorial(50000).value // ࣮ߦ
// res20: BigInt =
//33473205095971448369154760940714864779127732……
&WBMEFGFSϝιουΛ࢖ͬͯ͞ΒʹϦϑΝΫλϦϯά

15. &WBMʹΑΔ࠶ؼॲཧ
def factorial(n: BigInt): Eval[BigInt] =
if(n == 1) {
Eval.now(n)
} else {
Eval.defer(factorial(n - 1).map(_ * n))
}
factorial(50000).value // ࣮ߦ
// res20: BigInt =
//33473205095971448369154760940714864779127732……
factorial(50000).value
// res20: BigInt =
//33473205095971448369154760940714864779127732……
&WBMEFGFSϝιουΛ࢖ͬͯ͞ΒʹϦϑΝΫλϦϯά
ਖ਼ৗऴྃ

16. &WBMʹΑΔ࠶ؼॲཧ
&WBMEFGFSϝιου͸ԿΛ͍ͯ͠Δͷ͔ʁ
def defer[A](a: => Eval[A]): Eval[A] =
new Eval.Defer[A](a _) {}
sealed abstract class Defer[A](val thunk: () => Eval[A]) extends Eval[A] {
def memoize: Eval[A] = Memoize(this)
def value: A = evaluate(this)
}
&WBMΛฦ͢ܭࢉΛ஗Ԇ͍ͯ͠Δ
τϥϯϙϦϯԽ͕ߦΘΕɺؔ਺͕਺चͭͳ͗
Ͱݺ͹Εͳ͘ͳΔ

17. \$BUTΛษڧֶͯͯ͠Μͩ͜ͱ
ಡΜͩจݙ
ɾೣ൪IUUQFFETJODPNIFSEJOHDBUTKB
ɾ4DBMB8JUI\$BUT
ֶͼ
ɾϞφυͬͯԿͳͷ͔
ɾ,MFJTMJɺ3FBEFSϞφυ
ɾϞφυม׵ࢠ

18. ϞφυͬͯԿͳͷ͔
Ϟφυͱ͸ɺ݁߹཯ͱಉҰ཯Λຬͨ͢࠷খݶͷ
ϞφυίϯϏωʔλͷू·Γͷ͍ͣΕ͔Λ࣮૷ͨ͠΋ͷͰ͋Δɻ
ΧϥʔίοϓຊΑΓҾ༻

19. ϞφυͬͯԿͳͷ͔
Ϟφυͱ͸ɺ݁߹཯ͱಉҰ཯Λຬͨ͢࠷খݶͷ
ϞφυίϯϏωʔλͷू·Γͷ͍ͣΕ͔Λ࣮૷ͨ͠΋ͷͰ͋Δɻ
ʁ
ΧϥʔίοϓຊΑΓҾ༻

20. ϞφυͬͯԿͳͷ͔
ϞφυΛඥղ͘Ωʔϫʔυ
ɾϑΝϯΫλʔ 'VODUPS

ɾΞϓϦΧςΟϒϑΝϯΫλʔ "QQMJDBUJWF'VODUPS

͜ͷೋͭ͸ඞͣग़ͯ͘Δ
Ϟφυͱ͸ɺ݁߹཯ͱಉҰ཯Λຬͨ͢࠷খݶͷ
ϞφυίϯϏωʔλͷू·Γͷ͍ͣΕ͔Λ࣮૷ͨ͠΋ͷͰ͋Δɻ
ʁ
ΧϥʔίοϓຊΑΓҾ༻

21. Ϟφυ
'VODUPS"QQMZ
"QQMJDBUJWF'VODUPS.POBE
DBUTʹ͓͍ͯɺ࣍ͷॱͰਐԽ͍ͯ͘͠

22. 'VODUPS
-JTU 0QUJPOͳͲʹରͯ͠ɺ
แ·Εͨ஋ʹରͯ͠Ҿ਺ؔ਺Λద༻͢ΔॲཧΛఏڙ͢ΔܕΫϥε
List(1, 2, 3).map(_ * 2) // List(2, 4, 6)
Some(1).map(_ * 2) // Some(2)
@typeclass trait Functor[F[_]] extends functor.Invariant[F] { self =>
def map[A, B](fa: F[A])(f: A => B): F[B]
....
}

23. "QQMZ
'VODUPSΛ֦ுͯ͠ɺ/ݸͷ'VODUPS஋ʹ/Ҿ਺ؔ਺Λ
ద༻͢ΔॲཧΛఏڙ͢ΔܕΫϥε
import cats.implicits._
// implicit defͰ҉໧తʹmap2, map3͕ద༻͞ΕΔ
(Some(1), Some(2)).mapN(_ + ) // Some(3)
(Some(1), Some(2), Some(3)).mapN(_ * _ * _) // Some(6)
(List("ha", "heh", "hmm"), List("?", "!", ".")) mapN {_ + _}
// List(ha?, ha!, ha., heh?, heh!, heh., hmm?, hmm!, hmm.)

24. "QQMJDBUJWF'VODUPS
"QQMJDBUJWF͸"QQMZʹ
QVSFϝιου "'
Λ௥Ճ͍ͯ͠ΔܕΫϥε
ϓϦϛςΟϒ஋Λ"QQMJDBUJWFܕʹม׵ͯ͠ɺ
ଞͷ"QQMJDBUJWFͱ߹੒Ͱ͖Δɹ
DBUTͷ7BMJEBUFE͸BQQMJDBUJWFGVODUPSΛܗ੒͢Δ
1.pure[Option] // Some(1)
// શͯͷΠϕϯτΛݕূ্ͨ͠Ͱɺ߹੒Λߦ͏
val result = (valid[String, String]("event 1 ok") |@|
invalid[String, String]("event 2 failed!") |@|
invalid[String, String]("event 3 failed!")) map {_ + _ + _}
// result: cats.data.Validated[String,String] = Invalid(event 2 failed!event 3 failed!)
DPNQPTF G QVSF
G
DPNQPTF QVSF G
G
QVSF͸ಉҰ཯Λຬͨ͢

25. .POBE
"QQMJDBUJWF'VODUPS͸ෳ਺ͷ'VODUPS஋Λѻ͏͜ͱ͕Ͱ͖͕ͨɺ
લͷ'VODUPS஋ʹґଘ͢ΔॲཧΛॻ͚ͳ͍ɻ
def hogeOption(a: Int): Option[Int] = Some(a)
hogeOption(1).flatMap(a => hogeOption(a).map(b => a+b))
// Some(2)
for {
a b } yield (a + b)
// Some(2)
.POBE͸લͷ஋ʹґଘ͢ΔॲཧΛఏڙ͢ΔܕΫϥε
/POFqBU.BQ G
qBU.BQ H
/POFqBU.BQ BG B
qBU.BQ H

qBU.BQ͸݁߹཯Λຬͨ͢

26. Ϟφυ·ͱΊ
Ϟφυͱ͸ɺ݁߹཯ͱಉҰ཯Λຬͨ͢࠷খݶͷ
ϞφυίϯϏωʔλͷू·Γͷ͍ͣΕ͔Λ࣮૷ͨ͠΋ͷͰ͋Δɻ
DPNQPTF G QVSF
G
DPNQPTF QVSF G
G
ಉҰ཯
/POFqBU.BQ G
qBU.BQ H

/POFqBU.BQ BG B
qBU.BQ H

݁߹཯
஋ʹแ·Εͨ΋ͷʹରͯ͠ɺؔ਺ͷ࿈࠯Λ࣮ߦͰ͖Δ