Chapter 15 Implicit Parameters and Conversions

「Chapter 15 Implicit Parameters and Conversions」の編集履歴(バックアップ)一覧はこちら

Chapter 15 Implicit Parameters and Conversions」(2011/02/24 (木) 09:05:02) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

#co(){ Chapter 15 Implicit Parameters and Conversions Implicit parameters and conversions are powerful tools for custimizing existing libraries and for creating high-level abstractions. As an example, let's start with an abstract class of semi-groups that support an unspecified add operation. } * 第 15 章 暗黙の引数と暗黙の型変換 暗黙の引数と暗黙の型変換は、既存ライブラリのカスタマイズや高レベルの抽象に役立つ強力なツールです。例として、未特定の演算 add を持つ半群の抽象クラスからはじめましょう。 abstract class SemiGroup[A] { def add(x: A, y: A): A } #co(){ Here's a subclass Monoid of SemiGroup which adds a unit element. } SemiGroup に unit 要素を加えたサブクラス Monoid はこうなります。 abstract class Monoid[A] extends SemiGroup[A] { def unit: A } #co(){ Here are two implementations of monoids: } 次はモノイドの2つの実装です。 object stringMonoid extends Monoid[String] { def add(x: String, y: String): String = x.concat(y) def unit: String = "" } object intMonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 } #co(){ A sum method, which works over arbitrary monoids, can be written in plain Scala as follows. } 任意のモノイド上で動作する sum メソッドは、純粋な Scala で次のように書けます。 def sum[A](xs: List[A])(m: Monoid[A]): A = if (xs.isEmpty) m.unit else m.add(xs.head, sum(m)(xs.tail) #co(){ This sum method can be called as follows: } この sum メソッドは、次のように呼び出せます。 sum(List("a", "bc", "def"))(stringMonoid) sum(List(1, 2, 3))(intMonoid) #co(){ All this works, but it is not very nice. The problem is that the monoid implementations have to be passed into all code that uses them. We would sometimes wish that the system could figure out the correct arguments automatically, similar to what is done when type arguments are inferred. This is what implicit parameters provide. } これらはみんな動作しますが、いまいちです。問題は、それを使うコードすべてにモノイドの実装を渡さなくてはならないことです。型パラメータを推論してくれるのと同様に、システムが自動的に正しい引数を判別してくれたらいいのに、と思う場面がときどきあります。それこそが、暗黙の引数が提供するものです。 #co(){ Implicit Parameters: The Basics In Scala 2 there is a new implicit keyword that can be used at the beginning of a parameter list. Syntax: } ** 暗黙の引数 : 基本 (Implicit Parameters: The Basics) Scala 2では、引数リストのはじまりで使用できる新しいキーワード implicit があります。 ParamClauses ::= {'(' [Param {',' Param}] ')'} ['(' implicit Param {',' Param} ')'] #co(){ If the keyword is present, it makes all parameters in the list implicit. For instance, the following version of sum has m as an implicit parameter. } このキーワードがある場合、そのリストのすべての引数が暗黙の引数となります。たとえば、次のバージョンの sum では m は暗黙の引数となっています。 def sum[A](xs: List[A])(implicit m: Monoid[A]): A = if (xs.isEmpty) m.unit else m.add(xs.head, sum(xs.tail)) #co(){ As can be seen from the example, it is possible to combine normal and implicit parameters. However, there may only be one implicit parameter list for a method or constructor, and it must come last. } この例を見ると分かるように、通常の引数と暗黙の引数は組み合わせることができます。ただし暗黙の引数リストは、1つのメソッドまたはコンストラクタに対してただ1回だけ指定でき、かつ、それは末尾にこなくてはいけません。 #co(){ implicit can also be used as a modifier for definitions and declarations. Examples: } さらに、implicit は、定義や宣言に対する修飾子としても使えます。例をあげます。 implicit object stringMonoid extends Monoid[String] { def add(x: String, y: String): String = x.concat(y) def unit: String = "" } implicit object intMonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 } #co(){ The principal idea behind implicit parameters is that arguments for them can be left out from a method call. If the arguments corresponding to an implicit parameter section are missing, they are inferred by the Scala compiler. } 暗黙の引数の基本的な考え方は、メソッド呼び出しからその引数指定を省略する、というものです。もし、暗黙の引数セクションに対応する引数がなければ、Scala コンパイラによって推論されます。 #co(){ The actual arguments that are eligible to be passed to an implicit parameter are all identifiers X that can be accessed at the point of the method call without a prefix and that denote an implicit definition or parameter. } 暗黙の引数に渡されうる実際の引数は、メソッド呼び出しの時点でアクセス可能で、暗黙の定義や引数であると印が付けられた、プレフィックスのつかないすべての識別子 X です。 #co(){ If there are several eligible arguments which match the implicit parameter's type, the Scala compiler will chose a most specific one, using the standard rules of static overloading resolution. For instance, assume the call } もし、型が適合して暗黙の引数に渡しうる引数が複数ある場合、Scala コンパイラは静的オーバーロード解決の標準的な規則に従い、最も下位の特定的な引数を選びます。たとえば、次の呼び出しにおいて、 sum(List(1, 2, 3)) #co(){ in a context where stringMonoid and intMonoid are visible. We know that the formal type parameter a of sum needs to be instantiated to int. The only eligible value which matches the implicit formal parameter type Monoid[Int] is intMonoid so this object will be passed as implicit parameter. } stringMonoid と intMonoid が 見えている状況を考えてみます。sum の型パラメータ A は、int にインスタンス化される必要があります。形式上の暗黙の引数の型 Monoid[Int] に適合する値は intMonoid のみであり、この object が暗黙の引数として渡されます。 #co(){ This discussion also shows that implicit parameters are inferred after any type arguments are inferred. } この議論は、暗黙の引数の推論はすべての型パラメータが推論された後に行われる、ということも示しています。 #co(){ Implicit Conversions Say you have an expression E of type T which is expected to type S. T does not conform to S and is not convertible to S by some other predefined conversion. Then the Scala compiler will try to apply as last resort an implicit conversion I (E). Here, I is an identifier denoting an implicit definition or parameter that is accessible without a prefix at the point of the conversion, that can be applied to arguments of type T and whose result type conforms to the expected type S. } ** 暗黙の型変換 型 S であることが期待される型 T の式 E を考えましょう。T は、S に適合せず、事前定義された変換でも S に変換できないものとします。その場合、Scala コンパイラは、最後の手段として暗黙の型変換 I(E) の適用を試みます。ここで I は、暗黙定義または暗黙の引数であることを示す識別子で、変換の時点でプレフィックスなしでアクセスでき、型 T である引数に適用可能で、変換の結果が期待されている型 S に適合するものです。 #co(){ Implicit conversions can also be applied in member selections. Given a selection E.x where x is not a member of the type E, the Scala compiler will try to insert an implicit conversion I(E).x, so that x is a member of I(E). } 暗黙の型変換はメンバーの選択にも適用できます。選択 E.x が与えられ、ただしx は型 E のメンバーではない場合、Scala コンパイラは x が I(E) のメンバーとなるよう、暗黙の型変換 I(E).x の挿入を試みます。 #co(){ Here is an example of an implicit conversion function that converts integers into instances of class scala.Ordered: } 整数をクラス scala.Ordered のインスタンスに変換する、暗黙の型変換関数の例をあげます。 implicit def int2ordered(x: Int): Ordered[Int] = new Ordered[Int] { def compare(y: Int): Int = if (x < y1) 1 else if (x > y1) 1 else 0 } #co(){ View Bounds View bounds are convenient syntactic sugar for implicit parameters. Consider for instance a generic sort method: } ** 可視境界 可視境界は、暗黙の引数のための便利な糖衣構文です。たとえば、ジェネリックなソートメソッドを考えてみましょう。 def sort[A <% Ordered[A]](xs: List[A]): List[A] = if (xs.isEmpty || xs.tail.isEmpty) xs else { val {ys, zs} = xs.splitAt(xs.length / 2) merge(ys, zs) } #co(){ The view bounded type parameter [a <% Ordered[a]] expresses that sort is applicable to lists of type a such that there exists an implicit conversion from a to Ordered[a]. The definition is treated as a shorthand for the following method signature with an implicit parameter: } 可視境界をもつ型パラメータ [a <% Orderd[a]] は、型 a から Orderd[A] への暗黙の型変換が存在する型 a のリストに対して、sort が適用できることを表します。この定義は、次のような暗黙の引数を持つメソッドシグネチャの略記として扱われます。 def sort[A](xs: List[A])(implicit c: A => Ordered[A]): List[A] = ... #co(){ (Here, the parameter name c is chosen arbitrarily in a way that does not collide with other names in the program.) } (ここで、引数名 c は、プログラム内のほかの名前と衝突しない方法で任意に選ばれます) #co(){ As a more detailed example, consider the merge method that comes with the sort method above: } もっと詳しい例として、上記の sort に付随している merge メソッドを考えましょう。 def merge[A <% Ordered[A]](xs: List[A], ys: List[A]): List[A] = if (xs.isEmpty) ys else if (ys.isEmpty) xs else if (xs.head < ys.head) xs.head :: merge(xs.tail, ys) else if ys.head :: merge(xs, ys.tail) #co(){ After expanding view bounds and inserting implicit conversions, this method implementation becomes: } 可視境界が展開され暗黙の型変換が挿入されると、このメソッドの実装は次のようになります。 def merge[A](xs: List[A], ys: List[A]) (implicit c: A => Ordered[A]): List[A] = if (xs.isEmpty) ys else if (ys.isEmpty) xs else if (c(xs.head) < ys.head) xs.head :: merge(xs.tail, ys) else if ys.head :: merge(xs, ys.tail)(c) #co(){ The last two lines of this method definition illustrate two different uses of the implicit parameter c. It is applied in a conversion in the condition of the second to last line, and it is passed as implicit argument in the recursive call to merge on the last line. } このメソッド定義の終わりの2行が、暗黙の引数 c の2種類の使用例になっています。最後から2行目の条件における変換で適用され、最終行では merge の再帰呼び出しで暗黙の引数として渡されます。 #center(){[[前ページ>Chapter 14 Lazy Values]] [[ 15 章>Chapter 15 Implicit Parameters and Conversions]] [[目次>ScalaByExample和訳]] [[次ページ>Chapter 16 Hindley/Milner Type Inference]]} ---- - &quot;second to last&quot;は 最後から2行目 の意味です。 -- pomu0325 (2009-12-29 19:45:05) #comment
#co(){ Chapter 15 Implicit Parameters and Conversions Implicit parameters and conversions are powerful tools for custimizing existing libraries and for creating high-level abstractions. As an example, let's start with an abstract class of semi-groups that support an unspecified add operation. } #setmenu2(ex-r-menu) * 第 15 章 暗黙のパラメータと暗黙の型変換 暗黙のパラメータと暗黙の型変換は、既存ライブラリのカスタマイズや高レベルの抽象に役立つ強力なツールです。例として、未特定の演算 add を持つ半群の抽象クラスからはじめましょう。 abstract class SemiGroup[A] { def add(x: A, y: A): A } #co(){ Here's a subclass Monoid of SemiGroup which adds a unit element. } SemiGroup に unit 要素を加えたサブクラス Monoid はこうなります。 abstract class Monoid[A] extends SemiGroup[A] { def unit: A } #co(){ Here are two implementations of monoids: } 次はモノイドの2つの実装です。 object stringMonoid extends Monoid[String] { def add(x: String, y: String): String = x.concat(y) def unit: String = "" } object intMonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 } #co(){ A sum method, which works over arbitrary monoids, can be written in plain Scala as follows. } 任意のモノイド上で動作する sum メソッドは、純粋な Scala で次のように書けます。 def sum[A](xs: List[A])(m: Monoid[A]): A = if (xs.isEmpty) m.unit else m.add(xs.head, sum(m)(xs.tail) #co(){ This sum method can be called as follows: } この sum メソッドは、次のように呼び出せます。 sum(List("a", "bc", "def"))(stringMonoid) sum(List(1, 2, 3))(intMonoid) #co(){ All this works, but it is not very nice. The problem is that the monoid implementations have to be passed into all code that uses them. We would sometimes wish that the system could figure out the correct arguments automatically, similar to what is done when type arguments are inferred. This is what implicit parameters provide. } これらはみんな動作しますが、いまいちです。問題は、それを使うコードすべてにモノイドの実装を渡さなくてはならないことです。型引数を推論してくれるのと同様に、システムが自動的に正しい型引数を判別してくれたらいいのに、と思う場面がときどきあります。それこそが、暗黙のパラメータが提供するものです。 #co(){ Implicit Parameters: The Basics In Scala 2 there is a new implicit keyword that can be used at the beginning of a parameter list. Syntax: } #setmenu2(ex-r-menu) ** 暗黙のパラメータ : 基本 (Implicit Parameters: The Basics) Scala 2では、パラメータリストのはじまりで使用できる新しいキーワード implicit があります。 ParamClauses ::= {'(' [Param {',' Param}] ')'} ['(' implicit Param {',' Param} ')'] #co(){ If the keyword is present, it makes all parameters in the list implicit. For instance, the following version of sum has m as an implicit parameter. } このキーワードがある場合、そのリストのすべてのパラメータが暗黙のパラメータとなります。たとえば、次のバージョンの sum では m は暗黙の引数となっています。 def sum[A](xs: List[A])(implicit m: Monoid[A]): A = if (xs.isEmpty) m.unit else m.add(xs.head, sum(xs.tail)) #co(){ As can be seen from the example, it is possible to combine normal and implicit parameters. However, there may only be one implicit parameter list for a method or constructor, and it must come last. } この例を見ると分かるように、通常の引数と暗黙のパラメータは組み合わせることができます。ただし暗黙のパラメータリストは、1つのメソッドまたはコンストラクタに対してただ1回だけ指定でき、かつ、それは末尾にこなくてはいけません。 #co(){ implicit can also be used as a modifier for definitions and declarations. Examples: } さらに、implicit は、定義や宣言に対する修飾子としても使えます。例をあげます。 implicit object stringMonoid extends Monoid[String] { def add(x: String, y: String): String = x.concat(y) def unit: String = "" } implicit object intMonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 } #co(){ The principal idea behind implicit parameters is that arguments for them can be left out from a method call. If the arguments corresponding to an implicit parameter section are missing, they are inferred by the Scala compiler. } 暗黙のパラメータの基本的な考え方は、メソッド呼び出しからその引数指定を省略する、というものです。もし、暗黙のパラメータセクションに対応する引数がなければ、Scala コンパイラによって推論されます。 #co(){ The actual arguments that are eligible to be passed to an implicit parameter are all identifiers X that can be accessed at the point of the method call without a prefix and that denote an implicit definition or parameter. } 暗黙のパラメータに渡されうる実際の引数は、メソッド呼び出しの時点でアクセス可能で、暗黙の定義やパラメータであると印が付けられた、プレフィックスのつかないすべての識別子 X です。 #co(){ If there are several eligible arguments which match the implicit parameter's type, the Scala compiler will chose a most specific one, using the standard rules of static overloading resolution. For instance, assume the call } もし、型が適合して暗黙のパラメータに渡しうる引数が複数ある場合、Scala コンパイラは静的オーバーロード解決の標準的な規則に従い、最も下位の特定的な引数を選びます。たとえば、次の呼び出しにおいて、 sum(List(1, 2, 3)) #co(){ in a context where stringMonoid and intMonoid are visible. We know that the formal type parameter a of sum needs to be instantiated to int. The only eligible value which matches the implicit formal parameter type Monoid[Int] is intMonoid so this object will be passed as implicit parameter. } stringMonoid と intMonoid が 見えている状況を考えてみます。sum の型パラメータ a は、int にインスタンス化される必要があります。形式上の暗黙のパラメータの型 Monoid[Int] に適合する値は intMonoid のみであり、この object が暗黙のパラメータとして渡されます。 #co(){ This discussion also shows that implicit parameters are inferred after any type arguments are inferred. } この議論は、暗黙のパラメータの推論はすべての型引数が推論された後に行われる、ということも示しています。 #co(){ Implicit Conversions Say you have an expression E of type T which is expected to type S. T does not conform to S and is not convertible to S by some other predefined conversion. Then the Scala compiler will try to apply as last resort an implicit conversion I (E). Here, I is an identifier denoting an implicit definition or parameter that is accessible without a prefix at the point of the conversion, that can be applied to arguments of type T and whose result type conforms to the expected type S. } #setmenu2(ex-r-menu) ** 暗黙の型変換 型 S であることが期待される型 T の式 E を考えましょう。T は、S に適合せず、事前定義された変換でも S に変換できないものとします。その場合、Scala コンパイラは、最後の手段として暗黙の型変換 I(E) の適用を試みます。ここで I は、暗黙定義または暗黙のパラメータであることを示す識別子で、変換の時点でプレフィックスなしでアクセスでき、型 T である引数に適用可能で、変換の結果が期待されている型 S に適合するものです。 #co(){ Implicit conversions can also be applied in member selections. Given a selection E.x where x is not a member of the type E, the Scala compiler will try to insert an implicit conversion I(E).x, so that x is a member of I(E). } 暗黙の型変換はメンバーの選択にも適用できます。選択 E.x が与えられ、ただしx は型 E のメンバーではない場合、Scala コンパイラは x が I(E) のメンバーとなるよう、暗黙の型変換 I(E).x の挿入を試みます。 #co(){ Here is an example of an implicit conversion function that converts integers into instances of class scala.Ordered: } 整数をクラス scala.Ordered のインスタンスに変換する、暗黙の型変換関数の例をあげます。 implicit def int2ordered(x: Int): Ordered[Int] = new Ordered[Int] { def compare(y: Int): Int = if (x < y1) 1 else if (x > y1) 1 else 0 } #co(){ View Bounds View bounds are convenient syntactic sugar for implicit parameters. Consider for instance a generic sort method: } #setmenu2(ex-r-menu) ** 可視境界 可視境界は、暗黙のパラメータのための便利な糖衣構文です。たとえば、ジェネリックなソートメソッドを考えてみましょう。 def sort[A <% Ordered[A]](xs: List[A]): List[A] = if (xs.isEmpty || xs.tail.isEmpty) xs else { val {ys, zs} = xs.splitAt(xs.length / 2) merge(ys, zs) } #co(){ The view bounded type parameter [a <% Ordered[a]] expresses that sort is applicable to lists of type a such that there exists an implicit conversion from a to Ordered[a]. The definition is treated as a shorthand for the following method signature with an implicit parameter: } 可視境界をもつ型パラメータ [a <% Orderd[a]] は、型 a から Orderd[A] への暗黙の型変換が存在する型 a のリストに対して、sort が適用できることを表します。この定義は、次のような暗黙のパラメータを持つメソッドシグネチャの略記として扱われます。 def sort[A](xs: List[A])(implicit c: A => Ordered[A]): List[A] = ... #co(){ (Here, the parameter name c is chosen arbitrarily in a way that does not collide with other names in the program.) } (ここで、パラメータ名 c は、プログラム内のほかの名前と衝突しない方法で任意に選ばれます) #co(){ As a more detailed example, consider the merge method that comes with the sort method above: } もっと詳しい例として、上記の sort に付随している merge メソッドを考えましょう。 def merge[A <% Ordered[A]](xs: List[A], ys: List[A]): List[A] = if (xs.isEmpty) ys else if (ys.isEmpty) xs else if (xs.head < ys.head) xs.head :: merge(xs.tail, ys) else if ys.head :: merge(xs, ys.tail) #co(){ After expanding view bounds and inserting implicit conversions, this method implementation becomes: } 可視境界が展開され暗黙の型変換が挿入されると、このメソッドの実装は次のようになります。 def merge[A](xs: List[A], ys: List[A]) (implicit c: A => Ordered[A]): List[A] = if (xs.isEmpty) ys else if (ys.isEmpty) xs else if (c(xs.head) < ys.head) xs.head :: merge(xs.tail, ys) else if ys.head :: merge(xs, ys.tail)(c) #co(){ The last two lines of this method definition illustrate two different uses of the implicit parameter c. It is applied in a conversion in the condition of the second to last line, and it is passed as implicit argument in the recursive call to merge on the last line. } このメソッド定義の終わりの2行が、暗黙のパラメータ c の2種類の使用例になっています。最後から2行目の条件における変換で適用され、最終行では merge の再帰呼び出しで暗黙の引数として渡されます。 #center(){[[前ページ>Chapter 14 Lazy Values]] [[ 15 章>Chapter 15 Implicit Parameters and Conversions]] [[目次>ScalaByExample和訳]] [[次ページ>Chapter 16 Hindley/Milner Type Inference]]} ---- - &quot;second to last&quot;は 最後から2行目 の意味です。 -- pomu0325 (2009-12-29 19:45:05) #comment

表示オプション

横に並べて表示:
変化行の前後のみ表示:
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。