by shigemk2

当面は技術的なことしか書かない

関数型Scala(型クラス編) まとめ #関数型Scala

connpass.com

型クラスの話

型クラス入門

http://halcat0x15a.github.io/slide/functional_scala/#/

抽象化の手法のひとつ

Semigroupを例にScalaでの型クラスをみていく

型クラスはtraitで宣言する

trait Semigroup[A] {
  def append(x: A, y:A): A
}

結合則を満たしている

append(x, append(x,y)) == append(append(x,y),z)
  • 型クラスのインスタンスはimplicitをつけて宣言する
  • 型クラスのインスタンスは型を明示すべきである

Semigroupを使ったメソッドを定義 doubleメソッド 型クラスはカリー化されたimplicit パラメータにとります。

Scalazだとこんな感じの命名規則

implicitパラメータは型パラメータと同名の変数を付けることがある

implicitがスコープ内にあればdoubleメソッドは次のように呼び出すことができる 故に、IntかStringかどうかはあまり選ばない

assert(double(2) == 4)
assert(double("hoge") == "hogehoge")

implicitパラメータを明示しない記法も存在する

def quadruple[A: Semigroup](a: A) = double(double(a))

これはContext Bounds(暗黙的に引数がつくので、引数を省略できる)

二項演算は演算子として定義

implicit class SemigroupOps[A](x: A)(implicit A: Semigroup[A]) {
  def <>(y: A): A = A.append(x, y)
}

assert(1 <> 2 <> 3 == 6)
assert("foo" <> "bar" <> "baz" == "foobarbaz")

いろいろな方法からimplicitパラメータを検索できる

Monoidを例にimplicitの検索についてみてみる

モノイド - Wikipedia

trait Monoid[A] extends Semigroup[A] {
  def zero: A
}

メソッド定義(畳み込みでappendするメソッド アスタリスクで可変長引数を表現)

def append[A](xs: A*)(implicit A: Monoid[A]): A = xs.foldLeft(A.zero)(A.append)

ListをMonoidで抽象化してみる

implictはdefで宣言することで型パラメータをもつことが可能である

object Semigroup {

  implicit def listMonoid[A]: Monoid[List[A]] =
    new Monoid[List[A]] {
      def zero: List[A] = Nil
      def append(x: List[A], y: List[A]): List[A] = x ::: y
    }

}

こんな感じで使えます

assert(double(List(0, 1, 2)) == List(0, 1, 2, 0, 1, 2))

assert(append(List(0), List(1), List(2)) == List(0, 1, 2))
  • SemigroupとMonoidのどちらに対してもインスタンスを提供できる
  • 型クラス→スーパークラスのコンパニオンオブジェクト
  • MonoidはSemigroupに依存している 依存関係がよくわからない
  • 型クラスのコンパニオンオブジェクトとスーパークラスのコンパニオンオブジェクトだと検索順序があるのでは
  • インスタンスを定義しているときにいくつか検索するポイントがある
  • データ型のコンパニオンオブジェクト
  • 型クラススーパークラスのコンパニオンオブジェクト
  • 型クラスのコンパニオンオブジェクト

NEWTYPE

ひとつの型に関して複数のインスタンスが定義されることがある

implicit val sumMonoid: Monoid[Int] =
  new Monoid[Int] {
    def zero: Int = 0
    def append(x: Int, y: Int): Int = x + y
  }

implicit val productMonoid: Monoid[Int] =
  new Monoid[Int] {
    def zero: Int = 1
    def append(x: Int, y: Int): Int = x * y
  }

importによる明示的なインスタンスの選択

値クラスを使ってインスタンスが一意にしてもよい

case class Sum(value: Int) extends AnyVal

case class Product(value: Int) extends AnyVal

implicit val sumMonoid: Monoid[Sum] =
  new Monoid[Sum] {
    def zero: Sum = Sum(0)
    def append(x: Sum, y: Sum): Sum = Sum(x.value + y.value)
  }

implicit val productMonoid: Monoid[Product] =
  new Monoid[Product] {
    def zero: Product = Product(1)
    def append(x: Product, y: Product): Product = Product(x.value * y.value)
  }

IntのMonoidを数値に一般化

implicit def sumMonoid[A](implicit A: scala.math.Numeric[A]): Monoid[A] =
  new Monoid[A] {
    def zero: A = A.zero
    def append(x: A, y: A): A = A.plus(x, y)
  }

implicit def productMonoid[A](implicit A: scala.math.Numeric[A]): Monoid[A] =
  new Monoid[A] {
    def zero: A = A.one
    def append(x: A, y: A): A = A.times(x, y)
  }

implictは階層構造をつくることで優先順位をつけることができる

trait LowPrioritySemigroupImplicits {
  implicit def numeric[A](implicit A: scala.math.Numeric[A]): Monoid[A] =
    new Monoid[A] {
      def zero: A = A.zero
      def append(x: A, y: A): A = A.plus(x, y)
    }
}

object Semigroup extends LowPrioritySemigroupImplicits {
  implicit def int: Monoid[Int] =
    new Monoid[Int] {
      def zero: Int = 0
      def append(x: Int, y: Int): Int = x + y
    }
}

higher-kinded type

  • 型パラメータをとる型パラメータを指定できる
  • 例: Functor

mapを使った例

import scala.language.higherKinds

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

Functorは写像とよばれる

  • mapのように関数をカリー化されたパラメータにとることで型推論を効かせることができる
  • 型パラメータ[_]
implicit val listFunctor: Functor[List] =
  new Functor[List] {
    def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
  }

implicit val optionFunctor: Functor[Option] =
  new Functor[Option] {
    def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
  }

Functorを使った実装例

def pair[F[_], A](fa: F[A])(implicit F: Functor[F]): F[(A, A)] = F.map(fa)(a => (a, a))
assert(pair(List(0, 1, 2)) == List((0, 0), (1, 1), (2, 2)))

assert(pair(Option(0)) == Some((0, 0)))
  • EitherもFunctorとみなせる
  • Eitherは型パラメータを2つ取る
implicit def eitherFunctor[A]: Functor[({ type F[B] = Either[A, B] })#F] =
  new Functor[({ type F[B] = Either[A, B] })#F] {
    def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] = fa.right.map(f)
  }
  • 型の部分適用の記法
  • type F[B]
  • Either[A, B]
  • type F[B] = Either[A, B]

この例だと型を指定する必要がある(型を指定しないとコンパイルエラー)

type StringEither[A] = Either[String, A]

assert(pair(Right(0): StringEither[Int]) == Right((0, 0)))

まとめ

  • 型クラスを使いこなす
  • implicitを知る
  • Scalaの型推論を知る

俺的まとめ

  • Functor Monoid
  • implicit
  • コンパニオンオブジェクト

Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)

Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)

property based testingの話 まとめ #関数型Scala

connpass.com

Property Based Testing - scalapropsとscalacheckとその他色々

内容

  • property based testing と scalacheckの説明
  • scalacheckその不満点
  • scalaprops
  • Haskellの、その他様々な使い方の紹介

github.com

http://scalatest.org/user_guide/property_based_testing

  • Property = 性質
  • テストデータを半自動生成
  • 証明と普通のテストの中間のような?

  • 半自動 vs 全自動は後述

QuickCheck: Automatic testing of Haskell programs | Hackage

ScalaCheck: The Definitive Guide (English Edition)

ScalaCheck: The Definitive Guide (English Edition)

Scalacheckとは

  • 古くからある
  • specs2とかscalatestとかと組み合わせられる
  • 本体のテストにも使われる
  • Scala界隈唯一のProperty Based Testのライブラリ

  • D社でも使っていて、jsonやmsgpackのシリアライズやデシリアライズなどで

  • scalacheckのテストの書き方の例

forAll { l: List[String] =>
  l.reverse.reverse == l
}
  • 事前に関数を定義しておけば、任意の型の値が生成可能
  • List[String] は半自動で生成

Scalacheckがあるんだけど、不満があったので再発明したくなった

  • ScalazでもScalacheckをつかっている
  • 誰よりもScalacheckぽい

不満

  • CoArbitraryがない
  • 巨大な値を生成してしまうことがあり、テストが終わらないときの対策
  • seedを指定してランダムだけど予測可能なテスト
  • GenとArbitraryがあまり意味ない
  • ScalazのTypeclassの階層と重複テスト

  • 型クラスの階層があって、テストが重複しちゃう場合があったりする

d.hatena.ne.jp

Arbitraryとは

  • ランダムな値を生成するための型クラス(Haskellは型クラス Scalaは型クラスをデータとしても扱える Arbitrary自体がScalaの場合はMonad)

CoArbitrary

  • 関数自体の値をランダムに自動生成するために使うもの(何を言っているのかわからん)
class CoArbitrary a where
  coarbitrary :: a -> Gen b -> Gen b

StateMonadぽい

abstract class Cogen[A] {
  def cogen[B](a: A, g: CogenState[B]): CogenState[B]
}

case class CogenState[A](rand: Rand, gen: Gen[A])

Scalapropsの機能

  • CoAbitrary(Cogen)実装完了
  • タイムアウトとパラメータの細かい指定機能つけた
  • seed指定可能機能ついた
  • 重複テスト回避

github.com

github.com

出来る限りすべてのscalazのデフォルトのGenやCogenのインスタンスも組み込み→Gen.scalaがものすごく大きなプログラムとなってしまった

tab補完のきくsbt plugin

ここから本題

課題→Property Based Testingとは何か

  1. いろいろなケースをテスト
  2. 見つけたバグがわかりやすくあるべきShrinking
  3. 関数の扱い

Property Based Testing

  • Shrink(バグが見つかったらバグの最小ケース抽出を行う機能 scalacheckにもscalapropsにもある)
  • うまく行かないケースがままある

Tree生成してSVGで表示するやつ

Tree2SVG.scala · GitHub

うまくいかない

  • 生成にとても時間がかかる
  • 生成できずにプログラムが固まったように見える
  • メモリ足りずに死ぬ

ではどうする?→Haskellから学んでみる

  • 小さいものから順番にテストする

https://www.cs.york.ac.uk/fp/smallcheck/smallcheck.pdf

論文嫁!

  • 頑張ってデータ型に大きさという概念をもたせる
  • Shrinking自体が必要ない

  • LogicTというライブラリにバグがある気がするんで誰か助けて

smallcheckの問題点

  • 結局小さいものしかテストできないから柔軟性がない
  • 大きい場合だけ発生するバグだったらどうするのか

解決方法

論文嫁!

http://www.cse.chalmers.se/~almstroj/lic.pdf

quickcheckとsmallcheckの良いとこどり

アレフ数 - Wikipedia

全単射

関数の扱い

  • 関数のバグ→関数をShrinkingする?どうするの?

QuickCheckで関数のShrinkingができてうれしい github.com

  • ライブラリのAPI設計として使い勝手

新たなるShrinkの方法のご提案

  • SmartCheckの論文

https://www.cs.indiana.edu/~lepike/pubs/smartcheck.pdf

  • SubCheck github.com

curry

A Truly Integrated Functional Logic Language [CurryWiki]

結論

Shapeless覚えて、Haskellのライブラリの移植をscalapropsに入れよう!

f:id:shigemk2:20150725145851p:plain

Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)

Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)

何か作ろうと思ったけど無理だったので使ったライブラリ紹介 まとめ #関数型Scala

関数型Scalaの集い発表資料 · GitHub

  • Scalaといえば乙女ゲー?
  • QMA的な何かを作ろうと思った
  • ScalazとかScalaJSを使う
  • Scalaz-streamも

github.com

github.com

これを作ろうとした時に作ったライブラリ

  • http4s
  • doobie
  • scalaprops
  • argonaut
  • parboiled2
  • scalajs-react
  • (scalajs-dom)
  • scajajs-ace

d.hatena.ne.jp

doobieについてはここ読むといい tpolecat

まとめ

Scalaz, shapeless 縛りしててもなんだかんだ何か作れなくはなさそうな世の中であるらしい

補足

my-loadをFedora20 Scala 2.11.8とかで動かすと以下のエラーが出るので、まあだめなのでしょう。

java.lang.UnsupportedClassVersionError: com/typesafe/config/ConfigFactory : Unsupported major.minor version 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        at mylord.MyLordApp$.main(MyLordApp.scala:10)
        at mylord.MyLordApp.main(MyLordApp.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)

Free-ScalikeJDBC から見る合成可能なDSLの作り方 まとめ #関数型Scala

Free-ScalikeJDBC から見る合成可能なDSLの作り方 · GitHub

ScalikeJDBC

よりSQLっぽく書けるScala用JDBCライブラリ

サンプル

val (p, c) = (Programmer.syntax("p"), Company.syntax("c"))

val programmers: Seq[Programmer] = DB.readOnly { implicit session =>
  withSQL {
    select
      .from(Programmer as p)
      .leftJoin(Company as c).on(p.companyId, c.id)
      .where.eq(p.isDeleted, false)
      .orderBy(p.createdAt)
      .limit(10)
      .offset(0)
  }.map(Programmer(p, c)).list.apply()
}
  • DBライブラリはモナドにされたがる
  • 状態の変更が多いから…

d.hatena.ne.jp

Freeモナド

Free モナドとは、Functor を ベースに Monad を作れる構造

前提知識 Scalaz勉強会 主要な型クラスの紹介 · GitHub

Coyoneda scalaz/Coyoneda.scala at 7bbe2669267e992dc96d8e0e7e9e5d7c54a70033 · scalaz/scalaz · GitHub

Coproduct を利用した Free モナドの合成 d.hatena.ne.jp

natural transformation 自然変換 - Wikipedia

まとめ

    • -> * kind の型をつくる
  1. 1.で作った型をFreeでラップした値を返すメソッドをつくる
  2. Interpreter つくる

composable callbacks listeners メモ #関数型Scala

www.slideshare.net

  • CallbackとListener
  • 便利だけど重いので、複雑な処理をやらせようとするとコールバックヘルが起きる
  • 複雑な処理というか、多重ネスト
  • このコールバックヘルをなんとかしたい
  • 多重ネストとDRY
  • ネストが発生するそもそもの原因って
  • CallbackとListenerがComposableじゃない(小さな単位でごにょごにょできること)

CallbackやListenerを多重ネストから守ろう

いろいろな方法

f:id:shigemk2:20150725180344p:plain

ソースコード github.com

関数型Scalaの集いでクラスファイルについて話をした #関数型Scala

connpass.com

表題のとおりですが、Scalaのクラスファイルについて、発表しました。

shigemk2.github.io

発表内容自体は先月のKernelVM探検隊での発表の焼き直しです。

shigemk2.github.io

表紙を変えたのと、Seq Streamにvarとvalを追加しています。

発表者がそうそうたるメンバーのなか、ひとり雑魚が混じってもうこれわかんねぇな状態ですけども。

雑感

想像以上にモナモナしてました。あと、FP in Scalaは必読の模様でした。読書会の主催者だったくせに、内容を全部把握していないという体たらくで、他の発表者や参加者などをがっかりさせてしまった感があるので、反省します。

また、発表の焼き直しは、微妙にリスクを伴います。というのも、「これ前に発表したやんお前!」っていうツッコミが避けられないからです。幸い主催者の寛大な措置により、なんとか発表することが出来ました。ありがとうございます。

あと、FPと低レイヤーってあんまり相性よくない、というか、参加者の求めているものとは違うのかな、というのを肌で感じました。なんとなくですが。

最後に、Scalazのコミッターはよしださんの一人勝ちみたいな感じなので、年間10コミットくらいやれば、年間で2位になれるという知見を得られました。

まとめ

  • FP in Scalaを読もう
  • モナドを知ろう(Freeモナドとか)
  • 客層を考えながら発表内容を決めよう