Skip to content

Instantly share code, notes, and snippets.

@lyricallogical
Created November 20, 2012 15:53
Show Gist options
  • Save lyricallogical/4118752 to your computer and use it in GitHub Desktop.
Save lyricallogical/4118752 to your computer and use it in GitHub Desktop.
rpscala92
!SLIDE
implicit parameter 入門
----------------
!SLIDE
implicit parameter って?
----------------
* 「Type Classes がどうのこうの」
* 「Context Bounds がどうのこうの」
* 「Functor がどうのこうの」
* 「Monad がどうのこうの」
* 「CanBuildFrom がどうのこうの」
!SLIDE
わけがわかりません
----------------
これはそんな人のためのスライドです かんたんだよ
!SLIDE
ぐたいれい
----------------
* 文字列とか Json とかロギングするメソッドを定義する
* ロギングのために Logger を要求する
```scala
trait Logger { def log(msg: String): Unit }
def logString(str: String, logger: Logger) = ...
def logJson(jsValue: JsValue, logger: Logger) = ...
```
!SLIDE
気を利かせた(つもり)
----------------
* 「どうせ皆デフォルトの Logger 使うんでしょ」
```scala
object Logger { val defaultLogger: Logger = ... }
def logString(str: String, logger: Logger = Logger.defaultLogger) = ...
def logJson(jsValue: JsValue, logger: Logger = Logger.defaultLogger) = ...
```
!SLIDE
ところがどっこい
----------------
* 「誰がデフォルトの Logger なんて使うか」
* 思いは通じなかった…
```scala
logJson(json, new FooLogger)
logString((json \ "name").as[String], new FooLogger)
logJson(json \ "mother", new FooLogger)
```
!SLIDE
reconsidering default parameter
----------------
* コンテキストによって「デフォルト」は変わりうる
* しかし、デフォルト引数の値は後から変更できない
* ダメダメのダメダメダメです
!SLIDE
def, def, def and def!
----------------
* メソッドを定義しまくってもいいけど…
* ダサい & だるい
```scala
def logStringWithFooLogger(str: String) = logString(str, new FooLogger)
def logJsonWithFooLogger(jsValue: JsValue) = logJson(jsValue, new FooLogger)
```
!SLIDE
implicit parameter にご登場願いましょう
----------------
* じゃーん(眠い)(ちょうど日付が変わる寸前です)
```scala
def logString(str: String)(implicit logger: Logger = Logger.defaultLogger) = ...
def logJson(jsValue: JsValue)(implicit logger: Logger = Logger.defaultLogger) = ...
```
!SLIDE
何が変わった?
----------------
* 外部からデフォルト値を変更することができるようになりました!
* implicit modifier がついてる値をコンパイラが探して
* 自動的に implicit parameter に渡してくれるよ
* 毎回渡さなくても良くなった!
```scala
{
implicit val defaultLogger = new FooLogger
logJson(json)
logString((json \ "name").as[String])
logJson(json \ "mother")(defaultLogger /* explicit に渡すこともできるよ */)
}
```
!SLIDE
おしまい
----------------
めでたしめでたし
!SLIDE
ではない
----------------
まだもうちょっとある がんばれ(これ書いてるボクが)
!SLIDE
fluentd とか流行ってますよね
----------------
* 突然の構造化 Logger
* さてどうしたものか
```scala
trait JsonLogger { def log(msg: JsValue): Unit }
```
!SLIDE
generic!
----------------
* ロギング対象が汎用的な trait を用意してあげよう
```scala
trait GenericLogger[T] { def log(t: T): Unit }
object Logger { val defaultLogger: GenericLogger[String] = ... }
object JsonLogger { val defaultLogger: GenericLogger[JsValue] = ... }
```
!SLIDE
再定義
----------------
```scala
def logString(str: String)(implicit logger: GenericLogger[String] = Logger.defaultLogger) = ...
def logJson(jsValue: JsValue)(implicit logger: GenericLogger[JsValue] = JsonLogger.defaultLogger) = ...
```
!SLIDE
generic, generic, generic and generic!
----------------
* もうついでにロギングを行うメソッド自体も generic にしてしまえ!
* でも、デフォルト引数はどうしよう…?
```scala
def log[T](t: T)(implicit logger: GenericLogger[T] /* ここに書ける適切なデフォルト値がない */) = ...
```
!SLIDE
companion object
----------------
* ある class もしくは trait と名前がおんなじオブジェクト
* ここで implicit modifier つけて定義しておくと、コンパイラが見つけてくれる
* 型毎にデフォルト引数を用意しているようなものですね
```scala
trait GenericLogger[T] { def log(t: T): Unit }
object GenericLogger {
implicit val defaultStringLogger: GenericLogger[String] = ...
implicit val defaultJsonLogger: GenericLogger[JsValue] = ...
}
```
!SLIDE
結果
----------------
* なんか綺麗さっぱりした
```scala
log(json)
log((json \ "name").as[String])
log(json \ "mother")
```
!SLIDE
贅沢な名前だね
----------------
* 今日からお前の名前は Loggable だよ
```scala
trait Loggable[T] { def log(t: T): Unit }
object Loggable {
implicit val defaultStringLoggable: Loggable[String] = ...
implicit val defaultJsonLoggable: Loggable[JsValue] = ...
}
```
!SLIDE
Loggable ってなんだ
----------------
* 型 T がロギング可能であることをあらわす trait
* こういう「ある型に対する操作を提供する仕組み」を Type Classes と
* 呼ぶような 呼ばないような
```scala
trait Loggable[T] { def log(t: T): Unit }
```
!SLIDE
ある型に対する操作の提供
----------------
* 継承/mixin も似たようなものじゃないの?
* あとからでも操作を提供できる
* 継承は型の定義時にしかできないよ
* implicit conversion も似たようなこと出来る
* けど今回は implicit parameter が主題だから話さないよ
!SLIDE
なんかしよう before
----------------
* ログだけとっててもしかたない
* なんか処理するぞ ロギングもするぞ
* process では Loggable 使わないけど log が必要としている…
* ので implicit paramter 書く必要があるけどだるい
* それに渡すだけで使いもしない変数があるのはダサい
```scala
def process[T](t: T)(implicit ev: Loggable[T] /* ダサい */) = {
...
log(t)
...
}
```
!SLIDE
なんかしよう after
----------------
* Context Bounds!
* 型パラメタ T がロギング可能であることを要求している
* 単なる syntax sugar なので展開後は before みたいになります
```scala
def process[T : Loggable /* スマート! */](t: T) = {
...
log(t)
...
}
```
!SLIDE
まとめ
----------------
* implicit parameter = デフォルト値を外から変更できる default parameter
* というのはいいすぎだけれど、まあ大体そんなものです
* それに型変数が付いたときに Type Classes とかいってかっこつけるのです
* 書くのだるいときは Context Bounds という型制約が使えます
* 手前味噌ですが「型に対する制約」という視点からの説明も
* [http://www.slideshare.net/lyrical_logical/scala-14863557](http://www.slideshare.net/lyrical_logical/scala-14863557)
!SLIDE
do you have some questions?
----------------
!SLIDE
おわり
----------------
ねむかった
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment