任意のタイプのコレクションを受け入れ、それを新しいコレクション (同じコレクション タイプで異なる要素タイプ) にマップするメソッドを作成しようとしていますCC[_]
が、非常に苦労しています。基本的に私は実装しようとしていますが、コレクション自体にはmap
実装していません。
質問
次のようなシグネチャを持つメソッドを実装しようとしています。
def map[CC[_], T, U](cct: CC[T], f: T => U): CC[U]
その使用法は次のとおりです。
map(List(1, 2, 3, 4), (_ : Int).toString) //would return List[String]
どこでもうまくいく答えに興味がCC
ありArray
、私の試み(下記)が最終的にうまくいかなかった理由に興味があります。
私の試み
(せっかちな人のために、以下で説明しますが、これを機能させるにはまったく失敗しています。繰り返しますが、問題は「どうすればそのようなメソッドを作成できるか?」ということです)。
私はこのように始めます:
scala> def map[T, U, CC[_]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] =
| cct map f
^
<console>:9: error: value map is not a member of type parameter CC[T]
cct map f
^
OK、それは理にかなっています-それCC
は通過可能であると言わなければなりません!
scala> def map[T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] =
| cct map f
<console>:10: error: type mismatch;
found : Traversable[U]
required: CC[U]
cct map f
^
エラー、OK! たぶん、実際にそのcbf
インスタンスを指定した場合。結局のところ、戻り値の型 ( To
) をCC[U]
次のように指定します。
scala> def map[T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] =
| cct.map(t => f(t))(cbf)
<console>:10: error: type mismatch;
found : scala.collection.generic.CanBuildFrom[CC[T],U,CC[U]]
required: scala.collection.generic.CanBuildFrom[Traversable[T],U,CC[U]]
cct.map(t => f(t))(cbf)
^
エラー、OK! それはより具体的なエラーです。使えそうです!
scala> def map[T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: CanBuildFrom[Traversable[T], U, CC[U]]): CC[U] =
| cct.map(t => f(t))(cbf)
map: [T, U, X, CC[X] <: Traversable[X]](cct: CC[T], f: T => U)(implicit cbf: scala.collection.generic.CanBuildFrom[Traversable[T],U,CC[U]])CC[U]
素晴らしい。私は私を持っていますmap
!これを使おう!
scala> map(List(1, 2, 3, 4), (_ : Int).toString)
<console>:11: error: Cannot construct a collection of type List[java.lang.String] with elements of type java.lang.String based on a collection of type Traversable[Int].
map(List(1, 2, 3, 4), (_ : Int).toString)
^
何だって?
観察
当時のトニー・モリスのこの点に関する観察は完全に的を射ていたと思わずにはいられません。彼が何を言ったの?彼は「それが何であれ、それは地図ではない」と言った。これが scalaz スタイルでいかに簡単か見てみましょう:
scala> trait Functor[F[_]] { def fmap[A, B](fa: F[A])(f: A => B): F[B] }
defined trait Functor
scala> def map[F[_]: Functor, A, B](fa: F[A], f: A => B): F[B] = implicitly[Functor[F]].fmap(fa)(f)
map: [F[_], A, B](fa: F[A], f: A => B)(implicit evidence$1: Functor[F])F[B]
それで
scala> map(List(1, 2, 3, 4), (_ : Int).toString)
<console>:12: error: could not find implicit value for evidence parameter of type Functor[List]
map(List(1, 2, 3, 4), (_ : Int).toString)
^
となることによって
scala> implicit val ListFunctor = new Functor[List] { def fmap[A, B](fa: List[A])(f: A => B) = fa map f }
ListFunctor: java.lang.Object with Functor[List] = $anon$1@4395cbcb
scala> map(List(1, 2, 3, 4), (_ : Int).toString)
res5: List[java.lang.String] = List(1, 2, 3, 4)
自分へのメモ:トニーに聞け!