scala notes (7) - Advanced Type and Implicit

- advanced types

  • singleton type
def setTitle(title: String): this.type = { ...; this } // for subtypes

def set(obj: Title.type): this.type = { useNextArgAs = obj; this } //take object as parameter, no Title type

  • type projection
class Network {
    class Member(val name: String) {
        val contacts = new ArrayBuffer[Network#Member] // Member of any Network object
    }
    ...
}
  • Path must be stable
• A package
• An object
• A val
• this, super, super[S], C.this, C.super, or C.super[S]
var chatter = new Network
val fred = new chatter.Member // error, chatter should be declared as val instead of var
  • type alias
type Index = HashMap[String, (Int, Int)]
  • structural types
def appendLines(target: { def append(str: String): Any }, //with method defined
lines: Iterable[String]) {
    for (l <- lines) { target.append(l); target.append("\n") }
}
  • compound type
val image = new ArrayBuffer[java.awt.Shape with java.io.Serializable]
trait ImageShape extends Shape with Serializable
Shape with Serializable { def contains(p: Point): Boolean } // with structural type
  • existential type
Array[T] forSome { type T <: JComponent } // same as Array[_ <: JComponent]
Map[T, U] forSome { type T; type U <: T }
def process[M <: n.Member forSome { val n: Network }](m1: M, m2: M) = (m1, m2) //members from same network object
  • self type
trait Logged {
    def log(msg: String)
}
trait LoggedException extends Logged {
    this: Exception => // trait only can be mixed into subclass of Exception
    def log() { log(getMessage()) }
    // OK to call getMessage because this is an Exception
}
  • dependency injection (cake pattern)

supply component trait for each service that contains,

  1. any dependent components, expressed as self types
  2. a trait defines service interface
  3. an abstract val will be instantiated with an instance of service
  4. optionally, implementation of the service interface
trait LoggerComponent {
    trait Logger { ... }
    val logger: Logger
    class FileLogger(file: String) extends Logger { ... }
    ...
}
trait AuthComponent {
    this: LoggerComponent => // Gives access to logger
    trait Auth { ... }
    val auth: Auth
    class MockAuth(file: String) extends Auth { ... }
    ...
}
object AppComponents extends LoggerComponent with AuthComponent { //trait order doesn't matter
    val logger = new FileLogger("test.log")
    val auth = new MockAuth("users.txt")
}
  • abstract types (more suitable for the case that type is expected to be specified in subclass, compared to type parameter which is more suitable when type needs to be supplied at class instantiation)
trait Reader {
    type Contents
    def read(fileName: String): Contents // defined by abstract type Contents which is to be specified in subclass
}
class StringReader extends Reader {
    type Contents = String
    def read(fileName: String) = Source.fromFile(fileName, "UTF-8").mkString
}
class ImageReader extends Reader {
    type Contents = BufferedImage
    def read(fileName: String) = ImageIO.read(new File(fileName))
}
  • family polymorphism
trait ListenerSupport {
    type S <: Source
    type E <: Event
    type L <: Listener //abstract types
    trait Event {
        var source: S = _
    }
    trait Listener {
        def occurred(e: E): Unit
    }
    trait Source {
        this: S =>
        private val listeners = new ArrayBuffer[L]
        def add(l: L) { listeners += l }
        def remove(l: L) { listeners -= l }
        def fire(e: E) {
            e.source = this
            for (l <- listeners) l.occurred(e)
        }
    }
}
object ButtonModule extends ListenerSupport {
    type S = Button
    type E = ButtonEvent
    type L = ButtonListener //actual types
    class ButtonEvent extends Event
    trait ButtonListener extends Listener // ButtonEven in method occurred
    class Button extends Source {
        def click() { fire(new ButtonEvent) }
    }
}
object Main {
    import ButtonModule._
    def main(args: Array[String]) {
        val b = new Button
        b.add(new ButtonListener {
            override def occurred(e: ButtonEvent) { println(e) }
        })
        b.click()
    }
}
  • Higher-Kinded Type
type constructor, List -> List[Int]

higher-kinded type,

trait Iterable[E, C[_]] {
    def iterator(): Iterator[E]
    def build[F](): C[F]
    def map[F](f : (E) => F) : C[F]
}

real example:

trait Container[E] {//to hold values of E
    def +=(e: E): Unit
}
trait Iterable[E, C[X] <: Container[X]] { // with type bound of Container
    def iterator(): Iterator[E]
    def build[F](): C[F]
    def map[F](f : (E) => F) : C[F] = {
        val res = build[F]()
        val iter = iterator()
        while (iter.hasNext) res += f(iter.next())
        res
    }
}
class Buffer[E : ClassTag] extends Iterable[E, Buffer] with Container[E] {//classtag for instantiating a new array
    private var capacity = 10
    private var length = 0
    private var elems = new Array[E](capacity) // See note
    def iterator() = new Iterator[E] {
        private var i = 0
        def hasNext = i < length
        def next() = { i += 1; elems(i - 1) }
    }
    def build[F : ClassTag]() = new Buffer[F]
    def +=(e: E) {
        if (length == capacity) {
                capacity = 2 * capacity
                val nelems = new Array[E](capacity) // See note
                for (i <- 0 until length) nelems(i) = elems(i)
                elems = nelems
        }
        elems(length) = e
        length += 1
    }
}

- Implicit

implicit def int2Fraction(n: Int) = Fraction(n, 1)
val result = 3 * Fraction(4, 5) // Calls int2Fraction(3)
  • enrich existing class
val contents = new File("README").read
implicit class RichFile(val from: File) extends AnyVal{
    def read = Source.fromFile(from.getPath).mkString
}

  • rules of implicit conversion
  1. type of expression differs from the expected type  -> 3 * Fraction(4, 5)// Calls fraction2Double
  2. object accesses non-existing member -> 3.den // Calls int2Fraction
  3. object invokes method whose arguments don't match given arguments -> Fraction(4, 5) * 3 // Calls int2Fraction
  4. convert only when needed.
  5. no multiple conversion
  6. no ambiguous conversion
  • implicit parameter
def quote(what: String)(implicit delims: Delimiters) =
  delims.left + what + delims.right

compiler looks for value of Delimiters from (1# has precedence over 2#), 

  1. Among all val and def of the desired type that are in scope without a prefix. (
    • Implicits defined in current scope
    • Explicit imports
    • wildcard imports
    )
  2.  In the companion object of a type that is associated with the desired type. Associated types include the desired type itself and, if it is a parameterized type, its type parameters. (
    • Companion objects of a type
    • Implicit scope of an argument’s type (2.9.1)
    • Implicit scope of type arguments (2.8.0)
    • Outer objects for nested types
    )
  3. implicit in subclass has precedence over one in parent class
see https://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html for details
  • implicit conversion with implicit parameter
def smaller[T](a: T, b: T)(implicit order: T => Ordered[T]) // we have implicit method T => Ordered[T] defined
= if (order(a) < b) a else b
  • context bound
class Pair[T : Ordering](val first: T, val second: T) {
    def smaller(implicit ord: Ordering[T]) =
        if (ord.compare(first, second) < 0) first else second
}
or
class Pair[T : Ordering](val first: T, val second: T) {
    def smaller =
        if (implicitly[Ordering[T]].compare(first, second) < 0) first else second
}
  • type class
trait NumberLike[T] {
    def plus(x: T, y: T): T
    def divideBy(x: T, n: Int): T
}

object NumberLike {
    implicit object NumberLikeDouble extends NumberLike[Double] {
        def plus(x: Double, y: Double) = x + y
        def divideBy(x: Double, n: Int) = x / n
    }
    implicit object NumberLikeBigDecimal extends NumberLike[BigDecimal] {
        def plus(x: BigDecimal, y: BigDecimal) = x + y
        def divideBy(x: BigDecimal, n: Int) = x / n
    }
}
def average[T](x: T, y: T)(implicit ev: NumberLike[T]) =
  ev.divideBy(ev.plus(x, y), 2)  

or

def average[T : NumberLike](x: T, y: T) = {
    val ev = implicitly[NumberLike[T]]
    ev.divideBy(ev.plus(x, y), 2)
}

  • evidence
def firstLast[A, C](it: C)(implicit ev: C <:< Iterable[A]) = //instance of C <:< Iterable[A] is implicitly defined.
    (it.head, it.last)

















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章