HOW TO GET ALONG WITH IMPLICIT
Taisuke Oe / @OE_uia
WHO AM I?
Taisuke Oe @OE_uia
A gleaner as:
- the chairperson in ScalaMatsuri
- VPoE in F-CODE, INC.
Sometimes, a glean instructor as:
- a technical advisor in Septeni Original, Inc.
いろんな組織で落ち穂拾いしてます
RECAP: WHAT IS IMPLICIT?
TWO TYPES OF IMPLICIT
Implicit parameter
Implicit conversion
SIMPLE IMPLICIT PARAMETER EXAMPLE
from Scala Standard Library:
trait Seq[A]{
def sorted[B >: A](implicit ord: math.Ordering[B]): Seq[A]
}
scala> Seq(2,1,3).sorted
//Seq(1,2,3)
ord: math.Ordering[B] argument is passed implicitly.
Seq#sorted は最も単純な暗黙の引数の例
SIMPLE IMPLICIT CONVERSION EXAMPLE
from Scala Standard Library:
import scala.collection.JavaConverters._
val jList:java.util.List[Int] = List(1,2,3).asJava
IMPLICIT
Why is Implicit regarded as dif cult?
なぜ暗黙は難しいと思われがちなんだろう?
WHY?
- `Implicit` is NOT really explicit about what happens.
You'll need some tools to gure it out.
- `Implicit` scope is a bit complicated.
Scaladoc cannot explain `Implicit` values in its scope.
- `Implicit` is often designed to be induced.
暗黙の難しさ。
1) 何が起きているか明示されない 2) スコープがちょっと複雑 3)しばしば導出を前提にデザインされる
AGAIN, SIMPLE IMPLICIT PARAMETER EXAMPLE
Seq(2,1,3).sorted
//Seq(1,2,3)
Check with "-Xprint:typer" scalac option.
先程の例に戻り、どのように暗黙の値が解決されているか調べてみましょう。
-XPRINT:TYPER
DEMO
-XPRINT:TYPER
Sample.scala
object Main{
Seq(2,1,3).sorted
}
$ scalac -Xprint:typer Sample.scala
[[syntax trees at end of typer]] // Sample.scala
package <empty> {
object Main extends scala.AnyRef {
def <init>(): Main.type = {
Main.super.<init>();
()
};
scala.collection.Seq.apply[Int](2, 1, 3).sorted[Int](math.this.Ordering
}
}
`-Xprint:typer` allows us to gure out how implicit is resolved.
どのように暗黙の値の解決されてるか、分かる。
FOR MORE COMPLICATED PROJECTS
- Import working examples to IntelliJ IDEA
- Use `Implicit Parameter` function (Implicit Analyzer)
複雑なプロジェクトには、IntelliJ IDEAでSImplicit Parameterを調べようという機能がオススメ。
IMPLICIT ANALYZER
DEMO
HOW ABOUT IMPLICIT CONVERSION CASE?
- As long as it's implemented in *Enrich my library pattern*, easy stuff.
- *Enrich my library pattern* - Convert to a wrapper class with a new method you want.
- *Go to Declarations* in IntelliJ, or something similar in your editor.
Enrich my libraryパターンなら簡単に定義場所に飛べる
HOW ABOUT IMPLICIT CONVERSION CASE?
- Non *Enrich my library pattern* implicit conversion is not recommended.
- Ah, good luck.
- You can use `Implicit Conversions` in IntelliJ,
Enrich my libraryパターンじゃない暗黙の型変換は、そもそも非推奨
TIPS 1
Find working examples and analyze them.
動いているコードを探すのが、暗黙解決を理解するのに手っ取り早いでしょう
FIND WORKING EXAMPLES
Source codes that can compile.
- project codes by your team.
- Getting Started
- examples in OSS project repositories
- test codes
サンプルコードを見つける場所候補。
WHERE DOES ORDERING[INT] INSTANCE
COME FROM?
package scala.math
object Ordering {
trait IntOrdering extends Ordering[Int] {
def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y)
}
implicit object Int extends IntOrdering
}
scala.math.Ordering.Int is in its Implicit scope.
scala.math.Ordering.Intが暗黙のスコープ内に定義されているので、先程の例はコンパイルできるのです。
TIPS 2
Get familiar with common ways to include implicit values in the scope.
暗黙の値をスコープに加える代表的な方法を理解しよう。
IMPLICIT SCOPE?
Explicit imports / de nition
Comapnion modules of associated types.
EXPLICIT IMPORTS / DEFINITION
import scala.concurrent._
import ExecutionContext.Implicits.global
Future(1+2)
Import values de ned in a certain object. (Its name is sometimes obviouse like *Implicits*)
暗黙の値が定義されているobjectの値をimportしよう。Implicitsみたいな分かりやすい名前が推奨
COMAPNION MODULES OF ASSOCIATED TYPES
Quotes from Scala Language Speci cation 7.2 Implicit parameter
The implicit scope of a type T consists of all companion modules of classes
The parts of a type T are:
if T is a compound type T1 with …… with Tn, the union of the parts of T1,
if T is a parameterized type S[T1,…,Tn], the union of the parts of SS and
if T is a singleton type p.type, the parts of the type of p;
if T is a type projection S#U, the parts of S as well as T itself;
if T is a type alias, the parts of its expansion;
if T is an abstract type, the parts of its upper bound;
if T denotes an implicit conversion to a type with a method with argument
the parts of quantified (existential or universal) and annotated types are define
in all other cases, just T itself.
暗黙のスコープは、Scala言語仕様にまとまっています。
COMPANION OBJECTS OF ... WHAT?
You don't need to understand *an associated type* 100%.
Let's get along with common / ordinal patterns, one by one.
「関連のある型」のパターンを一緒に見てみよう
FREQUENTLY USED IMPLICIT SCOPES
PARAMETERIZED TYPE
trait A
object A {
implicit val ba:B[A] = new B[A]{}
}
trait B[T]
object B {
implicit val bc:B[C] = new B[C]{}
}
trait C
implicitly[B[A]]
implicitly[B[C]]
B[A]は、B,A両方のコンパニオンオブジェクト
SUPER TYPE
trait A
trait B extends A
object A{
implicit val b:B = new B{}
}
implicitly[B]
型Bのスーパー型Aのコンパニオンオブジェクトもスコープ内
PACKAGE OBJECT
package O
trait A
package object P{
implicit val a:A = new A{}
}
package P{
object B {implicitly[A]}
}
FOR REFERENCE.
TYPE A
trait A
object A{
implicit val a:A = new A{}
}
implicitly[A]
型A自身のコンパニオンオブジェクト
COMPOUND TYPE
trait A
object A{
implicit val ac:A with C = new A with C{}
}
trait C
implicitly[A with C]
A and C.
合成型A with Cは、AとC両方のコンパニオンオブジェクト。
TYPE ALIAS
trait A
object A{
implicit val a:A = new A{}
}
type D = A
implicitly[D]
ABSTRACT TYPE WITH UPPER BOUND
trait A
trait ASub extends A
trait B {type D <: A}
object A extends B{
type D = ASub
implicit val a:ASub = new ASub{}
}
implicitly[A.D]
IMPLICIT CONVERSION
trait A
object A {
implicit def f(a:A):B = new B{}
implicit def g(b:B):A = new A{}
}
trait B
val a:A = new B{}
//NG
//val b:B = new A{}
暗黙の型変換の、引数の型A
IMPLICIT CLASS
trait A
object A {
implicit class B(a:A)
}
implicitly[A => A.B]
val b:A.B = new A{}
TYPE PROJECTION
trait A { trait E }
object A {
private val a:A = new A{}
implicit val e:a.E = new a.E{}
}
implicitly[A#E]
SINGLETON TYPE
object A {
implicit val a:A.type = this
}
implicitly[A.type]
Associated type of A.type is A.
WHERE DID WE FIND ORDERING[INT]?
package scala.math
object Ordering {
trait IntOrdering extends Ordering[Int] {
def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y)
}
implicit object Int extends IntOrdering
}
In companion object of `Ordering`.
Orderingのコンパニオンオブジェクトに定義していました。
DEFINE YOUR OWN DATA TYPE.
case class User(name:String)
scala> Seq(User("B"),User("A")).sorted
//ERROR since `Ordering[User]` instance is NOT defined within Implicit scope.
Seq[User] can't be sorted since there is no Ordering[User] instance.
User型を定義しただけだと、Seq[User]をsortedすることはできません。
DEFINE ORDERING[USER] INSTANCE.
case class User(name:String)
object User{
//Get an Ordering[User] from Ordering[User]
implicit val ordering:Ordering[User] = Ordering.by(_.name)
}
scala> Seq(User("B"),User("A")).sorted
// Seq(User(A), User(B))
Ordering[User]型を得ることで、Seq[User]をsortedすることができました。
De ne Ordering[User] instance in companion object of `User`.
Ordering[User]型を得ることで、Seq[User]をsortedすることができました。
HOW DID WE DEFINE ORDERING[USER] INSTANCE?
implicit val ordering:Ordering[User] = Ordering.by(_.name)
object Ordering{
def by[T, S](f: (T) ⇒ S)(implicit ord: Ordering[S]): Ordering[T]
}
trait Ordering[T]{
def on[U](f: (U) ⇒ T): Ordering[U]
}
Watch out: direction of argument functions
Ordering型のインスタンスは、既存のOrdering型から作ることができる。
WAYS TO GET FROM EXISTING IMPLICIT VALUES
There are so many ways to do so:
Remember map , contramap and imap .
MAP AND CONTRAMAP
trait SerializedFormat
trait Reads[T]{
def read(arg:SerializedFormat):T
def map[U](f:T => U):Reads[U]
}
trait Writes[T]{
def writes(t:T):SerializedFormat
def contramap[U](f: U => T):Writes[U]
}
`T` is in :
return value position -> `map`
argument position -> `contramap`
型パラメータが戻り値のポジションの場合は`map`, 引数のポジションの場合は `contramap`
EXAMPLE: ORDERING[T]
package scala.math
trait Ordering[T] {
//abstract method
def compare(x: T, y: T): Int
/* ... */
}
Since `T` is in argument position, `Ordering.by` and `Ordering#on` are the same as `contramap`.
Orderingの場合も例外ではなく、Tが引数ポジションだから`contramap` 相当のメソッド。
OTHER EXAMPLES OF MAP AND CONTRAMAP
play-json: Reads[T], Writes[T]
ScalikeJDBC: TypeBinder[T], ParameterBinderFactory[T]
IMAP
trait Semigroup[T]{
//Like a standard Semigroup.
def append(x:T,y:T):T
//Like an InvariantFunctor
def imap[A](f: T => A)(g: A => T): Semigroup[A]
}
`T` is in both of return value and argument position -> `imap`
もしTが引数と戻り値の両方のポジションに出てくるなら、それは `imap` の出番だ
OTHER EXAMPLES OF IMAP
ScalikeJDBC: Binders[T] as xmap
DERIVING (TYPECLASS) INSTANCES FROM OTHERS
play-json uses macro to derive instances.
e.g. Json.reads[T], Json.writes[T]
shapeless uses HList to derive instances.
e.g. shapeless-contrib
AUTOMATIC DERIVATION EXAMPLE
case class Value(i:Int)
import MonoidSyntax._
import Monoid.typeClass._
Value(10) |+| Value(12)
//Value(22)
TIPS 3
Know basic ways to modify existing implicit values.
(Brand-new de nition would be second option.)
できるだけ既存の値を加工しよう
CONCLUSION
- `Implicit` is NOT really explicit about what happens.
=> Find working examples, and analyze them.
- `Implicit` scope is a bit complicated.
=> Get familiar with common ways to include implicit values in the scope.
- `Implicit` is often designed to be induced.
=> Know basic ways to modify existing implicit values.
暗黙とうまく付き合うための第一歩として、紹介したようなポイントを抑えましょう

How to get along with implicits

  • 1.
    HOW TO GETALONG WITH IMPLICIT Taisuke Oe / @OE_uia
  • 2.
    WHO AM I? TaisukeOe @OE_uia A gleaner as: - the chairperson in ScalaMatsuri - VPoE in F-CODE, INC. Sometimes, a glean instructor as: - a technical advisor in Septeni Original, Inc. いろんな組織で落ち穂拾いしてます
  • 3.
    RECAP: WHAT ISIMPLICIT?
  • 4.
    TWO TYPES OFIMPLICIT Implicit parameter Implicit conversion
  • 5.
    SIMPLE IMPLICIT PARAMETEREXAMPLE from Scala Standard Library: trait Seq[A]{ def sorted[B >: A](implicit ord: math.Ordering[B]): Seq[A] } scala> Seq(2,1,3).sorted //Seq(1,2,3) ord: math.Ordering[B] argument is passed implicitly. Seq#sorted は最も単純な暗黙の引数の例
  • 6.
    SIMPLE IMPLICIT CONVERSIONEXAMPLE from Scala Standard Library: import scala.collection.JavaConverters._ val jList:java.util.List[Int] = List(1,2,3).asJava
  • 7.
    IMPLICIT Why is Implicitregarded as dif cult? なぜ暗黙は難しいと思われがちなんだろう?
  • 8.
    WHY? - `Implicit` isNOT really explicit about what happens. You'll need some tools to gure it out. - `Implicit` scope is a bit complicated. Scaladoc cannot explain `Implicit` values in its scope. - `Implicit` is often designed to be induced. 暗黙の難しさ。 1) 何が起きているか明示されない 2) スコープがちょっと複雑 3)しばしば導出を前提にデザインされる
  • 9.
    AGAIN, SIMPLE IMPLICITPARAMETER EXAMPLE Seq(2,1,3).sorted //Seq(1,2,3) Check with "-Xprint:typer" scalac option. 先程の例に戻り、どのように暗黙の値が解決されているか調べてみましょう。
  • 10.
  • 11.
  • 12.
    $ scalac -Xprint:typerSample.scala [[syntax trees at end of typer]] // Sample.scala package <empty> { object Main extends scala.AnyRef { def <init>(): Main.type = { Main.super.<init>(); () }; scala.collection.Seq.apply[Int](2, 1, 3).sorted[Int](math.this.Ordering } } `-Xprint:typer` allows us to gure out how implicit is resolved. どのように暗黙の値の解決されてるか、分かる。
  • 13.
    FOR MORE COMPLICATEDPROJECTS - Import working examples to IntelliJ IDEA - Use `Implicit Parameter` function (Implicit Analyzer) 複雑なプロジェクトには、IntelliJ IDEAでSImplicit Parameterを調べようという機能がオススメ。
  • 14.
  • 15.
    HOW ABOUT IMPLICITCONVERSION CASE? - As long as it's implemented in *Enrich my library pattern*, easy stuff. - *Enrich my library pattern* - Convert to a wrapper class with a new method you want. - *Go to Declarations* in IntelliJ, or something similar in your editor. Enrich my libraryパターンなら簡単に定義場所に飛べる
  • 16.
    HOW ABOUT IMPLICITCONVERSION CASE? - Non *Enrich my library pattern* implicit conversion is not recommended. - Ah, good luck. - You can use `Implicit Conversions` in IntelliJ, Enrich my libraryパターンじゃない暗黙の型変換は、そもそも非推奨
  • 17.
    TIPS 1 Find workingexamples and analyze them. 動いているコードを探すのが、暗黙解決を理解するのに手っ取り早いでしょう
  • 18.
    FIND WORKING EXAMPLES Sourcecodes that can compile. - project codes by your team. - Getting Started - examples in OSS project repositories - test codes サンプルコードを見つける場所候補。
  • 19.
    WHERE DOES ORDERING[INT]INSTANCE COME FROM? package scala.math object Ordering { trait IntOrdering extends Ordering[Int] { def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y) } implicit object Int extends IntOrdering } scala.math.Ordering.Int is in its Implicit scope. scala.math.Ordering.Intが暗黙のスコープ内に定義されているので、先程の例はコンパイルできるのです。
  • 20.
    TIPS 2 Get familiarwith common ways to include implicit values in the scope. 暗黙の値をスコープに加える代表的な方法を理解しよう。
  • 21.
    IMPLICIT SCOPE? Explicit imports/ de nition Comapnion modules of associated types.
  • 22.
    EXPLICIT IMPORTS /DEFINITION import scala.concurrent._ import ExecutionContext.Implicits.global Future(1+2) Import values de ned in a certain object. (Its name is sometimes obviouse like *Implicits*) 暗黙の値が定義されているobjectの値をimportしよう。Implicitsみたいな分かりやすい名前が推奨
  • 23.
    COMAPNION MODULES OFASSOCIATED TYPES Quotes from Scala Language Speci cation 7.2 Implicit parameter The implicit scope of a type T consists of all companion modules of classes The parts of a type T are: if T is a compound type T1 with …… with Tn, the union of the parts of T1, if T is a parameterized type S[T1,…,Tn], the union of the parts of SS and if T is a singleton type p.type, the parts of the type of p; if T is a type projection S#U, the parts of S as well as T itself; if T is a type alias, the parts of its expansion; if T is an abstract type, the parts of its upper bound; if T denotes an implicit conversion to a type with a method with argument the parts of quantified (existential or universal) and annotated types are define in all other cases, just T itself. 暗黙のスコープは、Scala言語仕様にまとまっています。
  • 24.
    COMPANION OBJECTS OF... WHAT? You don't need to understand *an associated type* 100%. Let's get along with common / ordinal patterns, one by one. 「関連のある型」のパターンを一緒に見てみよう
  • 25.
  • 26.
    PARAMETERIZED TYPE trait A objectA { implicit val ba:B[A] = new B[A]{} } trait B[T] object B { implicit val bc:B[C] = new B[C]{} } trait C implicitly[B[A]] implicitly[B[C]] B[A]は、B,A両方のコンパニオンオブジェクト
  • 27.
    SUPER TYPE trait A traitB extends A object A{ implicit val b:B = new B{} } implicitly[B] 型Bのスーパー型Aのコンパニオンオブジェクトもスコープ内
  • 28.
    PACKAGE OBJECT package O traitA package object P{ implicit val a:A = new A{} } package P{ object B {implicitly[A]} }
  • 29.
  • 30.
    TYPE A trait A objectA{ implicit val a:A = new A{} } implicitly[A] 型A自身のコンパニオンオブジェクト
  • 31.
    COMPOUND TYPE trait A objectA{ implicit val ac:A with C = new A with C{} } trait C implicitly[A with C] A and C. 合成型A with Cは、AとC両方のコンパニオンオブジェクト。
  • 32.
    TYPE ALIAS trait A objectA{ implicit val a:A = new A{} } type D = A implicitly[D]
  • 33.
    ABSTRACT TYPE WITHUPPER BOUND trait A trait ASub extends A trait B {type D <: A} object A extends B{ type D = ASub implicit val a:ASub = new ASub{} } implicitly[A.D]
  • 34.
    IMPLICIT CONVERSION trait A objectA { implicit def f(a:A):B = new B{} implicit def g(b:B):A = new A{} } trait B val a:A = new B{} //NG //val b:B = new A{} 暗黙の型変換の、引数の型A
  • 35.
    IMPLICIT CLASS trait A objectA { implicit class B(a:A) } implicitly[A => A.B] val b:A.B = new A{}
  • 36.
    TYPE PROJECTION trait A{ trait E } object A { private val a:A = new A{} implicit val e:a.E = new a.E{} } implicitly[A#E]
  • 37.
    SINGLETON TYPE object A{ implicit val a:A.type = this } implicitly[A.type] Associated type of A.type is A.
  • 38.
    WHERE DID WEFIND ORDERING[INT]? package scala.math object Ordering { trait IntOrdering extends Ordering[Int] { def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y) } implicit object Int extends IntOrdering } In companion object of `Ordering`. Orderingのコンパニオンオブジェクトに定義していました。
  • 39.
    DEFINE YOUR OWNDATA TYPE. case class User(name:String) scala> Seq(User("B"),User("A")).sorted //ERROR since `Ordering[User]` instance is NOT defined within Implicit scope. Seq[User] can't be sorted since there is no Ordering[User] instance. User型を定義しただけだと、Seq[User]をsortedすることはできません。
  • 40.
    DEFINE ORDERING[USER] INSTANCE. caseclass User(name:String) object User{ //Get an Ordering[User] from Ordering[User] implicit val ordering:Ordering[User] = Ordering.by(_.name) } scala> Seq(User("B"),User("A")).sorted // Seq(User(A), User(B)) Ordering[User]型を得ることで、Seq[User]をsortedすることができました。 De ne Ordering[User] instance in companion object of `User`. Ordering[User]型を得ることで、Seq[User]をsortedすることができました。
  • 41.
    HOW DID WEDEFINE ORDERING[USER] INSTANCE? implicit val ordering:Ordering[User] = Ordering.by(_.name) object Ordering{ def by[T, S](f: (T) ⇒ S)(implicit ord: Ordering[S]): Ordering[T] } trait Ordering[T]{ def on[U](f: (U) ⇒ T): Ordering[U] } Watch out: direction of argument functions Ordering型のインスタンスは、既存のOrdering型から作ることができる。
  • 42.
    WAYS TO GETFROM EXISTING IMPLICIT VALUES There are so many ways to do so: Remember map , contramap and imap .
  • 43.
    MAP AND CONTRAMAP traitSerializedFormat trait Reads[T]{ def read(arg:SerializedFormat):T def map[U](f:T => U):Reads[U] } trait Writes[T]{ def writes(t:T):SerializedFormat def contramap[U](f: U => T):Writes[U] } `T` is in : return value position -> `map` argument position -> `contramap` 型パラメータが戻り値のポジションの場合は`map`, 引数のポジションの場合は `contramap`
  • 44.
    EXAMPLE: ORDERING[T] package scala.math traitOrdering[T] { //abstract method def compare(x: T, y: T): Int /* ... */ } Since `T` is in argument position, `Ordering.by` and `Ordering#on` are the same as `contramap`. Orderingの場合も例外ではなく、Tが引数ポジションだから`contramap` 相当のメソッド。
  • 45.
    OTHER EXAMPLES OFMAP AND CONTRAMAP play-json: Reads[T], Writes[T] ScalikeJDBC: TypeBinder[T], ParameterBinderFactory[T]
  • 46.
    IMAP trait Semigroup[T]{ //Like astandard Semigroup. def append(x:T,y:T):T //Like an InvariantFunctor def imap[A](f: T => A)(g: A => T): Semigroup[A] } `T` is in both of return value and argument position -> `imap` もしTが引数と戻り値の両方のポジションに出てくるなら、それは `imap` の出番だ
  • 47.
    OTHER EXAMPLES OFIMAP ScalikeJDBC: Binders[T] as xmap
  • 48.
    DERIVING (TYPECLASS) INSTANCESFROM OTHERS play-json uses macro to derive instances. e.g. Json.reads[T], Json.writes[T] shapeless uses HList to derive instances. e.g. shapeless-contrib
  • 49.
    AUTOMATIC DERIVATION EXAMPLE caseclass Value(i:Int) import MonoidSyntax._ import Monoid.typeClass._ Value(10) |+| Value(12) //Value(22)
  • 50.
    TIPS 3 Know basicways to modify existing implicit values. (Brand-new de nition would be second option.) できるだけ既存の値を加工しよう
  • 51.
    CONCLUSION - `Implicit` isNOT really explicit about what happens. => Find working examples, and analyze them. - `Implicit` scope is a bit complicated. => Get familiar with common ways to include implicit values in the scope. - `Implicit` is often designed to be induced. => Know basic ways to modify existing implicit values. 暗黙とうまく付き合うための第一歩として、紹介したようなポイントを抑えましょう