scala> case class Entity(id: Long) // defined case class Entity scala> val list = List(Entity(3L), Entity(1L), Entity(4L), Entity(2L)) val list: List[Entity] = List(Entity(3), Entity(1), Entity(4), Entity(2)) scala> list.sorted 1 |list.sorted | ^ |No implicit Ordering defined for B | ... scala> given entityOrdering: Ordering[Entity] with | def compare(x: Entity, y: Entity) = | summon[Ordering[Long]].compare(x.id, y.id) | // defined object entityOrdering scala> list.sorted val res2: List[Entity] = List(Entity(1), Entity(2), Entity(3), Entity(4))
global: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global Given インスタンス:コンテキストの定義 given ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
T => U): U = xs.map(f).sum Scala 2 Scala 3 implicit class ListOps[T](list: List[T]) { def sumBy[U: Numeric](f: T => U) = list.map(f).sum } scala> case class Entity(id: Long, score: Int) // defined case class Entity scala> val list = List(Entity(3L, 100), Entity(1L, 90), Entity(4L, 40), Entity(2L, 50)) val list: List[Entity] = List(Entity(3,100), Entity(1,90), Entity(4,40), Entity(2,50)) scala> list.sumBy(_.score) val res1: Int = 280
では以下の条件のいずれかが成り立つとき、メソッドを参照可能 a. スコープ内で定義、継承またはインポートされているとき b. 参照中の特定の given インスタンスのメンバーとして与えられているとき c. 型 r の暗黙のスコープで定義されているとき (r.m として参照可能) d. 型 r の暗黙のスコープの特定の given インスタンスで定義されているとき (r.m として参照可 能) 拡張メソッド
クラスの Given インスタンスで定義。書きやすくなった scala> import scala.language.implicitConversions scala> case class A(value: Int) // defined case class A scala> given Conversion[Int, A] with | def apply(v: Int): A = new A(v) | // defined object given_Conversion_Int_A scala> val hoge: A = 3 val hoge: A = A(3) Scala 2 Scala 3 scala> import scala.language.implicitConversions scala> case class A(value: Int) // defined case class A scala> implicit def intToA(v: Int): A = A(v) def intToA(v: Int): A scala> val hoge: A = 3 val hoge: A = A(3)
T => U) を継承しているので、関数としても書ける abstract class Conversion[-T, +U] extends Function1[T, U]: /** Convert value `x` of type `T` to type `U` */ def apply(x: T): U given Conversion[Long, A] = v => A(v.toInt)
U でない場合 ◦ 例)式の型は Int だが、期待される型が A である • b) 型 T の式 e の e.m に関して、T がメンバー m を定義していない場合 ◦ 例)Int が A のメンバー value を定義していない 暗黙の型変換: 型変換されるケース scala> val hoge: A = 3 val hoge: A = A(3) scala> 3.value val res0: Int = 3
に関して、T が m を定義している がいずれも引数に適用できない場合 ◦ 例)B のメソッド add (引数 Long 型) に String 型の値を渡した (C の add を適用) scala> case class B(value: Long) { self => | def add(rhs: Long): B = self.copy(value + rhs) | } // defined case class B scala> case class C(value: String) { self => | def add(rhs: String): C = self.copy(value + rhs) | } // defined case class C scala> given Conversion[B, C] = b => C(b.value.toString) lazy val given_Conversion_B_C: Conversion[B, C] scala> import scala.language.implicitConversions scala> B(3).add("5") val res2: C = C(35)
A ?=> B で表記する Context Function scala> trait Context // defined trait Context scala> type ContextFunction[A] = Context ?=> A // defined alias type ContextFunction[A] = (Context) ?=> A
Int = x * 2 def func(x: Int): (Context) ?=> Int scala> val ctx = new Context {} val ctx: Context = anon$1@3dd76768 scala> func(3) -- Error: 1 |func(3) | ^ |no implicit argument of type Context was found for parameter of (Context) ?=> Int scala> func(3)(using ctx) val res0: Int = 6 scala> given Context = ctx lazy val given_Context: Context scala> func(3) val res1: Int = 6
メソッドでは、子タグにコンテキストとしてインデントを渡している ◦ div メソッドでは、子タグを生成するブロック init にコンテキスト Div を渡している /** Model for <div> tag */ class Div extends Tag: def toHtmlString = (indent: Indent) ?=> val childIndent = indent + 1 indent.toSpace + "<div>" + childs.map(child => child.toHtmlString(using childIndent)).mkString("\n", "\n", "\n") + indent.toSpace + "</div>" /** Represent <div> tag */ def div(init: Div ?=> Unit)(using t: Tag): Unit = given d: Div = Div() init t.add(d)
= T def result[T](using r: WrappedResult[T]): T = r extension [T](x: T) def ensuring(condition: WrappedResult[T] ?=> Boolean): T = assert(condition(using x)) x @main def runPostCondition(): Unit = /** Should be 6 */ println(Try(List(1, 2, 3).sum.ensuring(result == 6))) /** Should be error */ println(Try(List(1, 2).sum.ensuring(result == 6)))
ただし、空リストに対しては例外を吐いていいものとします。 https://github.com/taretmch/have-fun-scala3/blob/master/exercise/src/main/scala/exercise/chapter3/Question2.scala 練習問題2: Using def max[T](list: List[T]): T = ...
T の値 y より小さいかどうかを返すメソッド ◦ x > y: Boolean • 型 T: Ord の値 x が T の値 y より大きいかどうかを返すメソッド ◦ x < y: Boolean • 型 T: Ord のリスト list: List[T] の最大値を返すメソッド ◦ list.max: T ◦ ※ Question2 のメソッドを使ってもよい https://github.com/taretmch/have-fun-scala3/blob/master/exercise/src/main/scala/exercise/chapter3/Question3.scala 練習問題3: 拡張メソッド