Scala Type 暗黑化

While I try to further study the Advanced Scala course last few topic around Type, we got frustrated… I think I can understand them literally, however, when and how I should use them. They may more for us to read the native language code. I don’t think I got any chance to use them…

Interesting here, I am graduated from Engineering background. Though I am really admired those who learn fundamental subject as Mathematics or Computer engineering, I am hard to get myself focus on the theoretical part. Take Scala as example, I think it is a great work. At the very beginning, there should be only an idea of functional programing and existing JVM runtime. Compiler is defined first to fulfill the idea. When more features are implemented and new request come in, it is naturally to the point that we cannot enhance compiler forever, the basic build block provided by compiler is not so intuitive itself. However, it can be used to build up advance feature without enscope compiler itself. I believe below advance Scala type serve this purpose as building block. Unless you really construct some feature from scratch, bump your head to find an easier, more readable, less copy/paste code, more user friendly feature, you will not understand why those build block is there. LOL. At this stage, as an engineer, I don’t think I am at that stage.

Type member (vs Type parameter)

In short, instead define A[T] using generic type T, you can define A { type T }. In this case, you not expose the inner type T to user. More link for study:

  1. https://docs.scala-lang.org/tour/abstract-type-members.html
  2. https://stackoverflow.com/questions/3170821/abstract-types-versus-type-parameters
  class AnimalCollection {
    type AnimalType // abstract type member
    type BoundedAnimal <: Animal
    type SuperBoundedAnimal >: Dog <: Animal
    type AnimalC = Cat
  }

Inner Types & Path Dependent Types

It is class defined inside another class. You can create an instance assign to parent instance inner class. However, pay attention that different parent instance inner class is different while compiling.  To avoid it, need to use ParentClass#ChildClass.

Useful link: https://stackoverflow.com/questions/2693067/what-is-meant-by-scalas-path-dependent-types

  class Outer {
    class Inner
    object InnerObject
    type InnerType

    def print(i: Inner) = println(i)
    def printGeneral(i: Outer#Inner) = println(i)
  }

 Structural Types

Structural Type can be useful but slow…. As it use reflection. It kind of descript a class or function need a type with minimum requirement. Any type fulfill the description can be passed later.

Useful link: https://en.wikibooks.org/wiki/Scala/Structural_Typing

  type UnifiedCloseable = {
    def close(): Unit
  } // STRUCTURAL TYPE

  def closeQuietly(unifiedCloseable: UnifiedCloseable): Unit = unifiedCloseable.close()

Self Types

Self type is a kind of implementation for dependency injection. It is kind of wired syntax. But can mean that this class depends on another class. Once defined self class, self class’s method can be used directly. It is a design pattern called CAKE Pattern.

Useful Link for Cake Pattern: https://medium.com/rahasak/scala-cake-pattern-e0cd894dae4e

  trait Instrumentalist {
    def play(): Unit
  }
  trait Singer { this: Instrumentalist => // SELF TYPE whoever implements Singer to implement Instrumentalist
    // rest of the implementation or API
    def sing(): Unit
  }

F-Bounded Polymorphism

This concept I am not quite understand. So far, it is kind of another polymorphism instead of implicit type class. The Syntax not readable to me. Guess my mindset is more on traditional polymorphism. A rare keyword forSome in the below post. I did a search too and found answer here. It is commonly used together with Self type.

This post is good: https://blog.genuine.com/2020/01/ad-hoc-polymorphism-in-scala/

  trait Animal[A <: Animal[A]] { self: A =>
    def breed: List[Animal[A]]
  }
  class Cat extends Animal[Cat] {
    override def breed: List[Animal[Cat]] = ??? // List[Cat] !!
  }
  class Dog extends Animal[Dog] {
    override def breed: List[Animal[Dog]] = ??? // List[Dog] !!
  }

Higher Kinded Types

This can be very useful, we can pass a generic type as F[_]. Then we no need to duplicate our code for different higher Kinded Type. For example, containers.

  trait Monad[F[_], A] { // higher-kinded type class
    def flatMap[B](f: A => F[B]): F[B]
    def map[B](f: A => B): F[B]
  }

  implicit class MonadList[A](list: List[A]) extends Monad[List, A] {
    override def flatMap[B](f: A => List[B]): List[B] = list.flatMap(f)
    override def map[B](f: A => B): List[B] = list.map(f)
  }

  implicit class MonadOption[A](option: Option[A]) extends Monad[Option, A] {
    override def flatMap[B](f: A => Option[B]): Option[B] = option.flatMap(f)
    override def map[B](f: A => B): Option[B] = option.map(f)
  }

  def multiply[F[_], A, B](implicit ma: Monad[F, A], mb: Monad[F, B]): F[(A, B)] =
    for {
      a <- ma
      b <- mb
    } yield (a, b)
  /*
    ma.flatMap(a => mb.map(b => (a,b)))
   */

  println(multiply(List(1,2), List("a", "b")))
  println(multiply(Some(2), Some("scala")))

Reflection

Scala has its own Reflection. Reflection is necessary in certain scenario. But generally, it is slow… It is kind of run time load the class…

Useful Link: https://docs.scala-lang.org/overviews/reflection/overview.html

  case class Person(name: String) {
    def sayMyName(): Unit = println(s"Hi, my name is $name")
  }

  // 0 - import
  import scala.reflect.runtime.{universe => ru}

  // 1 - MIRROR
  val m = ru.runtimeMirror(getClass.getClassLoader)
  // 2 - create a class object = "description"
  val clazz = m.staticClass("lectures.part5ts.Reflection.Person") // creating a class object by NAME
  // 3 - create a reflected mirror = "can DO things"
  val cm = m.reflectClass(clazz)
  // 4 - get the constructor
  val constructor = clazz.primaryConstructor.asMethod
  // 5 - reflect the constructor
  val constructorMirror = cm.reflectConstructor(constructor)
  // 6 - invoke the constructor
  val instance = constructorMirror.apply("John")

  println(instance)

  // I have an instance
  val p = Person("Mary") // from the wire as a serialized object
  // method name computed from somewhere else
  val methodName = "sayMyName"
  // 1 - mirror
  // 2 - reflect the instance
  val reflected = m.reflect(p)
  // 3 - method symbol
  val methodSymbol = ru.typeOf[Person].decl(ru.TermName(methodName)).asMethod
  // 4 - reflect the method = can DO things
  val method = reflected.reflectMethod(methodSymbol)
  // 5 - invoke the method

  method.apply()

留下评论