Upload
taisuke-oe
View
896
Download
0
Embed Size (px)
Citation preview
AUXパターンをDOTTYで解決する
自己紹介麻植泰輔
ScalaMatsuri 座長
Septeni Original, Inc. 技術アドバイザー
7/6 新宿 Geek Lounge で 「Real World Android Akka」を 日本語で 喋ります!
BONX, INC. 所属
将軍家の皆さま、ご協賛ありがとうございました!
来年春頃(予定) ScalaMatsuri 2018 もぜひよろしくお願いします m(_ _)m
もしScalaMatsuri経由で入社した方が居た場合、ぜひ記事化させてください
@OE_uia
今日のトピックDottyの何が嬉しいの? の一部を紹介する型クラスの型パラメーター同士に依存関係がある場合
Scala 2.x => AuxパターンDotty a.k.a. Scala 3.x => ???
型クラス既存の型に対し、共通の振る舞いを後から定義する (アドホック多相を実現する)
ためのデザインパターン
trait Semigroup[A]{ def append(a1:A,a2:A):A } object Semigroup{ implicit val intGroup:Semigroup[Int] = new Semigroup[Int]{ def append(a1:Int,a2:Int):Int = a1 + a2 } } object SemigroupSyntax{ def append[A](a1:A, a2:A)(implicit S:Semigroup[A]):A = S.append(a1,a2) }
import SemigroupSyntax._
append(1,2) // 3
DEPENDENT METHOD TYPE引数の型に依存して、メソッドのシグネチャ(のうち、多くの場合は戻り値の型)を変化させることができる
型クラスと組み合わせるパターンを、Magnet Patternと呼ぶ
引数の型にマグネットがくっついて、戻り値の型が変化するイメージ?
型クラス + DEPENDENT METHOD TYPEの例Measurableという型クラスを使って、Dependent Method Typeを活用する例
trait Measurable[A]{ type Size def sizeOf(a:A):Size } object Measurable{ implicit val intSize:Measurable[Int] = new Measurable[Int]{ type Size = Int def sizeOf(i:Int):Int = i } implicit def seqSize[A]:Measurable[Seq[A]] = new Measurable[Seq[A]]{ type Size = Int def sizeOf(s:Seq[A]):Int = s.size } } object MeasurableSyntax{ def measure[A](a:A)(implicit M:Measurable[A]):M.Size = M.sizeOf(a) }
import MeasurableSyntax._
measure(Seq(1,2,3)) // 3 measure(1) // 1
型クラスを組み合わせたい…一つの引数リストに両方入れる?
さっきのSemigroupとMeasurableを組み合わせると、こんな感じ…?
def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A], S:Semigroup[M.Size]):M.Size S.append(M.sizeOf(a1),M.sizeOf(a2))
コンパイルが通らない!同じ引数リスト内の引数を参照できない
scala> def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A], S:Semigroup[M.Size]): | S.append(M.measure(a1),M.measure(a2)) <console>:32: error: illegal dependent method type: parameter may only be referenced in a subsequent parameter section def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A], S:Semigroup[M.Size]): ^
型クラスを組み合わせたい…(TAKE 2)複数の暗黙の引数リストを使う?
じゃあ subsequent parameter section ということは、こんな感じ?
def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A])(implicit S:Semigroup[M.Size S.append(M.sizeOf(a1),M.sizeOf(a2))
コンパイルが通らない!暗黙の引数リストは1つまで
<console>:1: error: an implicit parameter section must be last def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A])(implicit S:Semigroup[M.Size ^ <console>:1: error: multiple implicit parameter sections are not allowed def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A])(implicit S:Semigroup[M.Size ^
型クラスを組み合わせたい…(TAKE 3)AUXパターン!!
型メンバを型パラメーターへマッピングすることで、暗黙のパラメーターが持つ型パラメーター同士で依存させることができる
trait Measurable[A]{ type Size def sizeOf(a:A):Size } //ここまで同じ
object Measurable{ type Aux[A0,B0] = Measurable[A0]{type Size = B0} implicit val intAux:Measurable.Aux[Int,Int] = new Measurable[Int]{ type Size = Int def sizeOf(i:Int):Int = i } }
def sumSizes[A,Size](a1:A,a2:A)(implicit M:Measurable.Aux[A,Size], S:Semigroup[Size S.append(M.sizeOf(a1),M.sizeOf(a2))
コンパイル通る!…けど、ちょっとhacky。
DOTTY A.K.A. SCALA 3.0EPFLの小田好先生の研究室で開発を主導現在pre-alpha日々アクティブに開発されてるので、以下、 scalaVersion := "0.1.1-bin-20170511-cc4533d-NIGHTLY" で実験
Nightly Buildsを試せる!
IMPLICIT FUNCTION TYPE暗黙の引数を取る関数、を型で表現
trait Semigroup[A]{ def append(a1:A,a2:A):A } object Semigroup{ implicit val intGroup:Semigroup[Int] = new Semigroup[Int]{ def append(a1:Int,a2:Int):Int = a1 + a2 } } //ここまで先程の例と一緒
object SemigroupSyntax{ type Semigroupish[A] = implicit Semigroup[A] => A def append[A](a1:A, a2:A):Semigroupish[A] = {implicit S => S.append(a1,a2)} }
object SemigroupMeasurableSyntax { import SemigroupSyntax._
def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A]):Semigroupish[M.Size] = append(}
MULTIPLE IMPLICIT PARAMETER LISTSImplicit Function Typeのメソッド表現
いずれかの暗黙の引数 のみ に明示的に値を渡す場合、どの暗黙の引数リストに渡すか決定する構文が欲しいf.explicitly(...) ... ?
まだ提案段階だが、入る可能性高そう?
//今(5/11 nightly builds)はまだ出来ないが、そのうちこうやって書けるようになるはず def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A])(implicit S:Semigroup[M.Size S.append(M.sizeOf(a1),M.sizeOf(a2))
でも実は...一つの引数リストに両方入れる
Scala 2.x系ではコンパイルは通らなかったが…
object SemigroupMeasurableSyntax { def sumSizes[A](a1:A,a2:A)(implicit M:Measurable[A], S:Semigroup[M.Size]):M.Size S.append(M.sizeOf(a1),M.sizeOf(a2)) }
/(^O^)\defined trait Measurable defined module Measurable defined module MeasurableSyntax defined trait Semigroup defined module Semigroup defined module SemigroupSyntax defined module SemigroupMeasurableSyntax <- !?!?!?
Dottyではコンパイル通る!!!
結論DottyになればAuxパターンは不要になる(ハズ) :tada:型クラスの型パラメーター同士に依存関係がある場合
Scala 2.x => AuxパターンDotty a.k.a. Scala 3.x => 共通の暗黙の引数リスト || Implicit Function Type
注: Implicit Function Typeは、Auxパターンをなくすこと「だけ」が目的では有りません文脈から値を取り出すことを抽象化する、大変強力な構文です
をご参照のこと。小田好先生のブログ