抽象化と値クラスとTagged Typesと

halcat0x15a

kits

オレオレ型クラスライブラリ

抽象化はしたいしパフォーマンスはほしい <- むずかしい

trait Monad[F[_]] extends Functor[F] {
  def pure[A](a: A): F[A]
  def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
  def map[A, B](fa: F[A])(f: A => B): F[B] =
    flatMap(x => pure(f(x))) // <- おそい
}
implicit val listMonad: Monad[List] =
  new Monad[List] {
    def pure[A](a: A): List[A] = List(a)
    def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = fa.flatMap(f)
    override def map[A, B](fa: F[A])(f: A => B): F[B] =
      fa.map(f) // <- だいたいoverrideする
  }

implicit parameterは型クラスのみに使われるべき(過激派的意見)

つまりはimplicit valueは一意に決まってほしい

def sleep(implicit time: Long): Unit =
  Thread.sleep(time) // <- Longでは一意に決まらない

一意に決まるようにするには? -> newtype!

case class Time[Tag](toLong: Long) extends AnyVal

def sleep[T](implicit time: Time[T]): Unit =
  Thread.sleep(time.toLong)

Tag毎に違ったインスタンスを定義することで一意に決まる(型レベルの数があればなお良い)

値クラスと抽象化

結論: 値クラスは抽象化できない

  • “値クラスの値が、汎用トレイトを含む別の型として扱われるとき、値クラスのインスタンスの実体がインスタンス化される必要がある。”
  • “値クラスが型引数として使われる場合もこのルールがあてはまる。 例えば、identify を呼び出すだけでも Meter インスタンスの実体が作成されることが必要となる”

TODO :javapとかみせる

Tagをつけることでコンパイラには違った型にみえるけどコンパイル結果は同じ型になるということ

つまり効率がよい

TODO :javapとかみせる

しかしboxingとunboxingが起きてる

primitive型の場合はvalue classの方がよい?(要出典)

まとめ

  • implicit valueが一意に決めたいときは型をラップしよう
  • 値クラスの扱いに気をつけよう
  • tagged typesはboxingが起きるので気をつけよう