Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

How to get along with implicits

472 views

Published on

ScalaMatsuri 2018

Published in: Software
  • Be the first to comment

How to get along with implicits

  1. 1. HOW TO GET ALONG WITH IMPLICIT Taisuke Oe / @OE_uia
  2. 2. 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. いろんな組織で落ち穂拾いしてます
  3. 3. RECAP: WHAT IS IMPLICIT?
  4. 4. TWO TYPES OF IMPLICIT Implicit parameter Implicit conversion
  5. 5. 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 は最も単純な暗黙の引数の例
  6. 6. SIMPLE IMPLICIT CONVERSION EXAMPLE from Scala Standard Library: import scala.collection.JavaConverters._ val jList:java.util.List[Int] = List(1,2,3).asJava
  7. 7. IMPLICIT Why is Implicit regarded as dif cult? なぜ暗黙は難しいと思われがちなんだろう?
  8. 8. 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)しばしば導出を前提にデザインされる
  9. 9. AGAIN, SIMPLE IMPLICIT PARAMETER EXAMPLE Seq(2,1,3).sorted //Seq(1,2,3) Check with "-Xprint:typer" scalac option. 先程の例に戻り、どのように暗黙の値が解決されているか調べてみましょう。
  10. 10. -XPRINT:TYPER DEMO
  11. 11. -XPRINT:TYPER Sample.scala object Main{ Seq(2,1,3).sorted }
  12. 12. $ 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. どのように暗黙の値の解決されてるか、分かる。
  13. 13. FOR MORE COMPLICATED PROJECTS - Import working examples to IntelliJ IDEA - Use `Implicit Parameter` function (Implicit Analyzer) 複雑なプロジェクトには、IntelliJ IDEAでSImplicit Parameterを調べようという機能がオススメ。
  14. 14. IMPLICIT ANALYZER DEMO
  15. 15. 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パターンなら簡単に定義場所に飛べる
  16. 16. 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パターンじゃない暗黙の型変換は、そもそも非推奨
  17. 17. TIPS 1 Find working examples and analyze them. 動いているコードを探すのが、暗黙解決を理解するのに手っ取り早いでしょう
  18. 18. FIND WORKING EXAMPLES Source codes that can compile. - project codes by your team. - Getting Started - examples in OSS project repositories - test codes サンプルコードを見つける場所候補。
  19. 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. 20. TIPS 2 Get familiar with common ways to include implicit values in the scope. 暗黙の値をスコープに加える代表的な方法を理解しよう。
  21. 21. IMPLICIT SCOPE? Explicit imports / de nition Comapnion modules of associated types.
  22. 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. 23. 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言語仕様にまとまっています。
  24. 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. 25. FREQUENTLY USED IMPLICIT SCOPES
  26. 26. 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両方のコンパニオンオブジェクト
  27. 27. SUPER TYPE trait A trait B extends A object A{ implicit val b:B = new B{} } implicitly[B] 型Bのスーパー型Aのコンパニオンオブジェクトもスコープ内
  28. 28. PACKAGE OBJECT package O trait A package object P{ implicit val a:A = new A{} } package P{ object B {implicitly[A]} }
  29. 29. FOR REFERENCE.
  30. 30. TYPE A trait A object A{ implicit val a:A = new A{} } implicitly[A] 型A自身のコンパニオンオブジェクト
  31. 31. 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両方のコンパニオンオブジェクト。
  32. 32. TYPE ALIAS trait A object A{ implicit val a:A = new A{} } type D = A implicitly[D]
  33. 33. 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]
  34. 34. 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
  35. 35. IMPLICIT CLASS trait A object A { implicit class B(a:A) } implicitly[A => A.B] val b:A.B = new A{}
  36. 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. 37. SINGLETON TYPE object A { implicit val a:A.type = this } implicitly[A.type] Associated type of A.type is A.
  38. 38. 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のコンパニオンオブジェクトに定義していました。
  39. 39. 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することはできません。
  40. 40. 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することができました。
  41. 41. 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型から作ることができる。
  42. 42. WAYS TO GET FROM EXISTING IMPLICIT VALUES There are so many ways to do so: Remember map , contramap and imap .
  43. 43. 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`
  44. 44. 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` 相当のメソッド。
  45. 45. OTHER EXAMPLES OF MAP AND CONTRAMAP play-json: Reads[T], Writes[T] ScalikeJDBC: TypeBinder[T], ParameterBinderFactory[T]
  46. 46. 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` の出番だ
  47. 47. OTHER EXAMPLES OF IMAP ScalikeJDBC: Binders[T] as xmap
  48. 48. 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
  49. 49. AUTOMATIC DERIVATION EXAMPLE case class Value(i:Int) import MonoidSyntax._ import Monoid.typeClass._ Value(10) |+| Value(12) //Value(22)
  50. 50. TIPS 3 Know basic ways to modify existing implicit values. (Brand-new de nition would be second option.) できるだけ既存の値を加工しよう
  51. 51. 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. 暗黙とうまく付き合うための第一歩として、紹介したようなポイントを抑えましょう

×