2つのアプローチについての私の考え。
構造タイプ
の構造型を使用できますが、構造型が表示されないためforeach
、複数の型で機能するように構造型を作成できます。map
例えば:
import collection.generic.CanBuildFrom
object StructuralMap extends App {
type HasMapAndForeach[A] = {
// def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That
def foreach[B](f: (A) ⇒ B): Unit
}
def printValues(xs: HasMapAndForeach[Any]) {
xs.foreach(println _)
}
// def mapValues(xs: HasMapAndForeach[Any]) {
// xs.map(_.toString).foreach(println _)
// }
def forComp1(xs: HasMapAndForeach[Any]) {
for (i <- Seq(1,2,3)) println(i)
}
printValues(List(1,2,3))
printValues(Some(1))
printValues(Seq(1,2,3))
// mapValues(List(1,2,3))
}
scala> StructuralMap.main(new Array[String](0))
1
2
3
4
5
6
7
8
9
10
map
上でコメントアウトされたメソッドを参照してください。これは、暗黙的List
に型パラメーターとしてハードコーディングされています。CanBuildFrom
タイプを一般的に拾う方法があるかもしれません-私はそれをそこにあるScalaタイプの達人への質問として残しておきます。とを置き換えてみHasMapAndForeach
ましthis.type
たList
が、どちらも機能しませんでした。
構造タイプに関する通常のパフォーマンスの警告が適用されます。
Scalaz
構造タイプは行き止まりなので、サポートしたい場合はmap
、Travisのscalazアプローチを見て、それがどのように機能するかを見てみましょう。彼の方法は次のとおりです。
def printValues[F[_]: Each](xs: F[Int]) = xs foreach println
def incremented[F[_]: Functor](xs: F[Int]) = xs map (_ + 1)
(以下では、私が間違っている場合は訂正してください。これをscalaz学習体験として使用しています)
型クラスEach
とは、の型を、またはに暗黙的に使用できるFunctor
型に制限するために使用されます。たとえば、通話中F
Each[F]
Functor[F]
printValues(List(1,2,3))
コンパイラは、を満たす暗黙的なものを探しますEach[List]
。Each
特徴は
trait Each[-E[_]] {
def each[A](e: E[A], f: A => Unit): Unit
}
Each
オブジェクトには、暗黙のfor (Each[TraversableOnce]
のList
サブタイプでTraversableOnce
あり、特性は反変です)があります。
object Each {
implicit def TraversableOnceEach[A]: Each[TraversableOnce] = new Each[TraversableOnce] {
def each[A](e: TraversableOnce[A], f: A => Unit) = e foreach f
}
}
「コンテキストバインド」構文に注意してください
def printValues[F[_]: Each](xs: F[Int])
の省略形です
def printValues(xs: F[Int])(implicit ev: Each[F])
これらは両方とも、それが型クラスF
のメンバーであることを示していEach
ます。型クラスを満たす暗黙の値は、ev
パラメーターとしてprintValues
メソッドに渡されます。
printValues
orメソッドの内部では、typeパラメーターに上限または下限がないためincremented
、コンパイラーはorメソッドがあることを認識しませんxs
。それがわかる限り、コンテキストバウンドを満たします(型クラスの一部です)。持っているスコープには何がありますか? scalazからの両方とメソッドがあります:map
foreach
F
F
AnyRef
foreach
map
MA
foreach
map
trait MA[M[_], A] {
def foreach(f: A => Unit)(implicit e: Each[M]): Unit = e.each(value, f)
def map[B](f: A => B)(implicit t: Functor[M]): M[B] = t.fmap(value, f)
}
foreach
のおよびmap
メソッドは、または型クラスMA
によって制約されることに注意してください。これらは元のメソッドと同じ制約であるため、制約が満たされ、メソッドを介して暗黙の変換が行われます。Each
Functor
MA[F, Int]
maImplicit
trait MAsLow extends MABLow {
implicit def maImplicit[M[_], A](a: M[A]): MA[M, A] = new MA[M, A] {
val value = a
}
}
F
元のメソッドのタイプはタイプM
インになりMA
ます。
元の呼び出しに渡された暗黙のパラメーターは、暗黙のパラメーターとしてforeach
またはに渡されますmap
。の場合、foreach
そのeach
暗黙のパラメータで呼び出されますe
。上記の例では、元のパラメーターがであったため、暗黙のev
はタイプでした。同じタイプです。 に委任する呼び出し。_Each[TraversableOnce]
List
e
foreach
each
e
foreach
TraversableOnce
したがって、呼び出しの順序は次のprintValues(List(1,2,3))
とおりです。
new Each[TraversableOnce]
-> printValues
-> new MA
-> MA.foreach
-> Each.each
->TraversableOnce.foreach
彼らが言うように、余分なレベルの間接参照で解決できない問題はありません:)