Scala 2.10 からの型クラス2(Functorはどうする?)


前回の記事にコメントをいただいて、Functor とか Monad なんかの高階型はどうするのか改めて考えてみました。

結論から言うと Scala は関数リテラルで型パラメータとれないので、関数リテラルだけでなんとかしようというのは不可能っぽいです(いまんところの私の頭では)。

しかし、implicity 使ったり context-bound を使わなくてもなんとかなりそうです。ここまで来ると型クラスじゃねーだろっ。と言われるかもしれませんがアドホック多相にはなってると思います。

ということで、Functor をやってみます。Haskell だとこんな感じですね。

class Functor f where
    fmap :: (a -> b) -> f a -> f b

Scala だと

import language.higherKinds
 
trait Functor[A] {
  type F[_]
  def fmap[B](func: A => B): F[B]
}
trait FunctorFunctions {
  def fmap[A, B](func: A => B)(f: Functor[A]): m.F[B] = f.fmap(func)
}

Option 型に適用してみます。

trait FunctorInstance {
  implicit class OptionFunctor[A](option: Option[A]) extends Functor[A] {
    type F[_] = Option[_]
    def fmap[B](func: A => B): Option[B] = option.map(func)
  }
}

使い方。

val allInstances = new FunctorInstance with FunctorFunctions {}
import allInstances._
 
val maybeInt = Option(3)
val func: Int => Int = _ * 2
 
println(maybeInt.fmap(func))
println(fmap(func)(maybeInt))
 
// こんな風にも使えます
def doubleInt(f: Functor[Int]): f.F[Int] = f.fmap(func)
 
println(doubleInt(maybeInt))

def は撲滅できませんでしたが、context-bound が撲滅できるのが私個人としては嬉しいです。それにやっぱりコード量はこっちのほうが少ないし!

でも、この fmap、カリー化を用いた部分適用(という言い方でいい?)できない! ぎゃふん!

  1. この定義だと問題点は色々あると思いますけど、たとえばScalazでこういう定義

    https://github.com/scalaz/scalaz/blob/v7.0.4/core/src/main/scala/scalaz/Traverse.scala#L81

    def sequence[G[_]:Applicative,A](fga: F[G[A]])

    で、「任意のApplicativeを引数の制約として要求する」とか「任意のMonadを(ry」というのがありますけど、そういうのが、この型クラスの定義では自然に書けなくなります。

    Scalazだったら Functor[List] という形でオブジェクトを引き回せますけど

    trait Functor[A] {
    type F[_]
    def fmap[B](func: A => B): F[B]
    }

    において、F[_]の型だけ固定したくても、同時にAの型も固定しないといけなくなるので。
    (それを避ける、補う仕組みをすごく頑張れば入れられるかもしれないけど、そしたらコード増えて意味がなくなる)

Leave a Comment

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください