18
AUXパターンをDOTTYで解決する

AuxパターンをDottyで解決する

Embed Size (px)

Citation preview

Page 1: AuxパターンをDottyで解決する

AUXパターンをDOTTYで解決する

Page 2: AuxパターンをDottyで解決する

自己紹介麻植泰輔

ScalaMatsuri 座長

Septeni Original, Inc. 技術アドバイザー

7/6 新宿 Geek Lounge で 「Real World Android Akka」を 日本語で 喋ります!

BONX, INC. 所属

将軍家の皆さま、ご協賛ありがとうございました!

来年春頃(予定) ScalaMatsuri 2018 もぜひよろしくお願いします m(_ _)m

もしScalaMatsuri経由で入社した方が居た場合、ぜひ記事化させてください

@OE_uia

Page 3: AuxパターンをDottyで解決する

今日のトピックDottyの何が嬉しいの? の一部を紹介する型クラスの型パラメーター同士に依存関係がある場合

Scala 2.x => AuxパターンDotty a.k.a. Scala 3.x => ???

Page 4: AuxパターンをDottyで解決する

型クラス既存の型に対し、共通の振る舞いを後から定義する (アドホック多相を実現する)

ためのデザインパターン

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

Page 5: AuxパターンをDottyで解決する

DEPENDENT METHOD TYPE引数の型に依存して、メソッドのシグネチャ(のうち、多くの場合は戻り値の型)を変化させることができる

型クラスと組み合わせるパターンを、Magnet Patternと呼ぶ

引数の型にマグネットがくっついて、戻り値の型が変化するイメージ?

Page 6: AuxパターンをDottyで解決する

型クラス + 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

Page 7: AuxパターンをDottyで解決する

型クラスを組み合わせたい…一つの引数リストに両方入れる?

さっきの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))

Page 8: AuxパターンをDottyで解決する

コンパイルが通らない!同じ引数リスト内の引数を参照できない

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]): ^

Page 9: AuxパターンをDottyで解決する

型クラスを組み合わせたい…(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))

Page 10: AuxパターンをDottyで解決する

コンパイルが通らない!暗黙の引数リストは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 ^

Page 11: AuxパターンをDottyで解決する

型クラスを組み合わせたい…(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))

Page 12: AuxパターンをDottyで解決する

コンパイル通る!…けど、ちょっとhacky。

Page 13: AuxパターンをDottyで解決する

DOTTY A.K.A. SCALA 3.0EPFLの小田好先生の研究室で開発を主導現在pre-alpha日々アクティブに開発されてるので、以下、 scalaVersion := "0.1.1-bin-20170511-cc4533d-NIGHTLY" で実験

Nightly Buildsを試せる!

Page 14: AuxパターンをDottyで解決する

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(}

Page 15: AuxパターンをDottyで解決する

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))

Page 16: AuxパターンをDottyで解決する

でも実は...一つの引数リストに両方入れる

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)) }

Page 17: AuxパターンをDottyで解決する

/(^O^)\defined trait Measurable defined module Measurable defined module MeasurableSyntax defined trait Semigroup defined module Semigroup defined module SemigroupSyntax defined module SemigroupMeasurableSyntax <- !?!?!?

Dottyではコンパイル通る!!!

Page 18: AuxパターンをDottyで解決する

結論DottyになればAuxパターンは不要になる(ハズ) :tada:型クラスの型パラメーター同士に依存関係がある場合

Scala 2.x => AuxパターンDotty a.k.a. Scala 3.x => 共通の暗黙の引数リスト || Implicit Function Type

注: Implicit Function Typeは、Auxパターンをなくすこと「だけ」が目的では有りません文脈から値を取り出すことを抽象化する、大変強力な構文です

をご参照のこと。小田好先生のブログ