implicit
Scalaの修飾子: implicit val i = 2
他の言語には無い機能
Scalaの修飾子: implicit val i = 2
他の言語にはあまり無い機能
implicitによるScalaの拡張
scala> val foo = "foo" // String型の変数
foo: String = foo
scala> def hoge(i: Int)(j: Int) = i * j // (カリー化された風な)関数
hoge: (i: Int)(j: Int)Int
scala> val fuga = (x: Int) => hoge(2)(x) // (Int => Int)となる関数
fuga: Int => Int = <function1>
scala> fuga(3) // 関数呼び出し
res1: Int = 6
// trait: Rubyのモジュール、Javaの(実装が持てる)Interfaceみたいなの
scala> trait A {
| def f(i: Int): Int
| def twice(j: Int): Int = j*2
| }
defined trait A
// Intを取るコンストラクタを持つクラスに、traitをmixin
scala> class B(n: Int) extends A {
| def f(i: Int) = i * n
| }
defined class B
scala> val b = new B(3) // インスタンス生成
b: B = B@380910bc
scala> b.f(3) // クラスに定義された関数が呼び出せる
res5: Int = 9
scala> b.twice(5) // mixinしたtraitに定義された関数が呼び出せる
res6: Int = 10
前準備
implicit parameter
implicit conversion
暗黙のパラメータ
// implicitなInt型の変数を用意
scala> implicit val i = 2
i: Int = 2
// implicitパラメータを取る関数
scala> def foo(x: Int)(implicit y: Int) = x * y
foo: (x: Int)(implicit y: Int)Int
// なぜかimplicitな値が渡されてる
scala> foo(5)
res0: Int = 10
NOTICE
暗黙の型変換
// 暗黙の型変換を行う関数
scala> implicit def getString(i: Int): String = i.toString
warning: there was one feature warning; re-run with -feature for details
getString: (i: Int)String
scala> val i = 3
i: Int = 3
scala> val str: String = i
str: String = 3
scala> def bang(str: String) = str + "!!"
bang: (str: String)String
// String型の引数が要求されているので、暗黙の型変換が行われる
scala> bang(i)
res0: String = 3!!
暗黙の型変換まとめておいて、必要に応じてimport
するとか
pimp my libraryパターン
Context/Session/Connection等の引き回し
型クラスの実現
外部ライブラリにより提供されたクラスを拡張したい…
Javaの場合
Rubyの場合
1.hour.ago
とか
✘╹◡╹✘ ☆ pry
[1] pry(main)> "waiwai".bang
NoMethodError: undefined method `bang' for "waiwai":String
from (pry):1:in `__pry__'
[1] pry(main)> class String
[1] pry(main)* def bang
[1] pry(main)* self + "!!"
[1] pry(main)* end
[1] pry(main)* end
=> :bang
[2] pry(main)> "waiwai".bang
=> "waiwai!!"
Scalaの場合
scala> "waiwai".bang
<console>:8: error: value bang is not a member of String
"waiwai".bang
^
scala> implicit class RichString(str: String) {
| def bang = s"$str!!"
| }
defined class RichString
scala> "waiwai".bang
res1: String = waiwai!!
scala> "waiwai": { def bang: String }
res2: AnyRef{def bang: String} = RichString@75cd480d
// コネクションプールからコネクションを取得
def update(id: UserId, name: String) = DBAction { implicit session: DBSession =>
// 暗黙的パラメータの利用
val user = UserRepository.updateName(id, name)
Ok("")
}
object UserRepository {
val users: UserDAO = ???
// コネクションが渡される事前提に処理を記述
def update(userId: UserId, name: String)(implicit session: DBSession): Option[User] =
session.withTransaction {
users.findById(userId).map { user =>
users.insert(user.copy(name = name))
}
}
def searchByTeamId(teamId: TeamId)(implicit session: DBSession) = ???
...
}
Haskellの型クラスの仕組みをimplicitを用いてScalaで実現
trait Adder[A] { // plusという関数を持つ
def plus(a1: A, a2: A): A
}
// plusという関数を持つAdderを用いるだけで、型引数の中身を知らなくて良い
def adhocplus[A](a1: A, a2: A)(implicit adder: Adder[A]): A = adder.plus(a1, a2)
object SimpleAdder {
// Int用の実装
implicit val intAdder = new Adder[Int] {
def plus(a1: Int, a2: Int) = a1 + a2
}
// String用の実装
implicit val stringAdder = new Adder[Int] {
def plus(a1: String, a2: String) = a1 + a2 + "!!!"
}
}
import SimpleAdder._ // このスコープで有効な(Haskellでいう)インスタンスを明示できる
adhocplus(1, 3) // -> 4
adhocplus("aa", "bb") // -> "aabb!!!"
かっこいい型クラス
実例implicit: Play Framework
Content-Type
を決定
object Writeable extends DefaultWriteables {
// controllerの最後が凡そ OK("")、OK(json)
// BAD_REQUEST("")、BAD_REQUEST(Html("<html></html>")) などを呼びだす
// `transform`のA型の引数が、上記引数の型になる(String型だったり、Html型だったり)
// (これらを呼び出す時、ContentTypeOf は明示しなくて良い)
// mimeTypeが ContentTYpeOf[A] から呼び出されている
def apply[A](transform: A => Array[Byte])(implicit ct: ContentTypeOf[A]): Writeable[A] =
Writeable(transform, ct.mimeType)
}
object ContentTypeOf extends DefaultContentTypeOfs
trait DefaultContentTypeOfs {
implicit def contentTypeOf_Html(implicit codec: Codec): ContentTypeOf[Html] = {
ContentTypeOf[Html](Some(ContentTypes.HTML)) // -> "text/html"
}
implicit def contentTypeOf_JsValue(implicit codec: Codec): ContentTypeOf[JsValue] = {
ContentTypeOf[JsValue](Some(ContentTypes.JSON)) // -> "application/json"
}
implicit def contentTypeOf_String(implicit codec: Codec): ContentTypeOf[String] = {
ContentTypeOf[String](Some(ContentTypes.TEXT)) // -> "text/plain"
}
...
}
!!!!!!ここにScalaちゃんの画像!!!!!!