Try Cats

Ca4df28501e4c9cfbceb91f367afa784?s=47 fuzyco
January 11, 2018

Try Cats

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

Ca4df28501e4c9cfbceb91f367afa784?s=128

fuzyco

January 11, 2018
Tweet

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 ɾ৽͍͠σʔλܕ ɾ࣮༻্ඞཁͳશͯͷ൚༻ؔ਺ͷ࣮૷  ΞυϗοΫଟ૬ੑɺUSBJU JNQMJDJU ೣ൪ΑΓҾ༻ 4DBMB[ ɾ৽͍͠σʔλܕ

    ɾඪ४Ϋϥεͷ֦ு 0QUJPO0QT -JTU0QT  ɾ࣮༻্ඞཁͳશͯͷ൚༻ؔ਺ͷ࣮૷  ΞυϗοΫଟ૬ੑɺUSBJU JNQMJDJU ಠश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] = cats.Always@5212e1f5 // lazy valͷΑ͏ʹ஗ԆධՁ val z = Eval.later { println("Computing Z") math.random } // z: cats.Eval[Double] = cats.Later@33eda11
  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] = cats.Always@5212e1f5 // lazy valͷΑ͏ʹ஗ԆධՁ val z = Eval.later { println("Computing Z") math.random } // z: cats.Eval[Double] = cats.Later@33eda11
  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 <- hogeOption(1) b <- hogeOption(a) } 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 ݁߹཯ ஋ʹแ·Εͨ΋ͷʹରͯ͠ɺؔ਺ͷ࿈࠯Λ࣮ߦͰ͖Δ