2015年4月23日 星期四

Scala knowledge, <:< type

前言
Scala type system 可以有type constraints, 比如[ T <: Int ] 要 type T 是 Int後代, 不過有些更進階的符號, 看起來會以為是operator, 事實上是一個type的 syntax sugar進階應用, <:< 就是一個最好例子



<:< type

先簡單看一段code
 scala> def peek[C, A](col: C)(implicit ev: C <:< Traversable[A]) =  
    |  (col.head, col)  
 foo: [C,A](col: C)(implicit ev: <:<[C,Traversable[A]])(A, C)  


比較要注意的地方是 implicit ev: C <:< Traversable[A]
看起來是需要一個 implicit value ev他的type是C
然後C 又接著 <:< Traversable[A]
結果上來說
限制C是 subtype of Traversable[A]

這樣看起來<:< 就跟 <: subtype constraints一樣嘛
但實際上<:<  是一個type, 並非compiler層級的syntax

<:< type定義如下
 /**  
   * An instance of `A <:< B` witnesses that `A` is a subtype of `B`.  
   * Requiring an implicit argument of the type `A <:< B` encodes  
   * the generalized constraint `A <: B`.  
   *  
   * @note we need a new type constructor `<:<` and evidence `conforms`,  
   * as reusing `Function1` and `identity` leads to ambiguities in  
   * case of type errors (`any2stringadd` is inferred)  
   *  
   * To constrain any abstract type T that's in scope in a method's  
   * argument list (not just the method's own type parameters) simply  
   * add an implicit argument of type `T <:< U`, where `U` is the required  
   * upper bound; or for lower-bounds, use: `L <:< T`, where `L` is the  
   * required lower bound.  
   *  
   * In part contributed by Jason Zaugg.  
   */  
  @implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")  
  sealed abstract class <:<[-From, +To] extends (From => To) with Serializable  
  private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }  
  // The dollar prefix is to dodge accidental shadowing of this method  
  // by a user-defined method of the same name (SI-7788).  
  // The collections rely on this method.  
  implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]  
  @deprecated("Use `implicitly[T <:< U]` or `identity` instead.", "2.11.0")  
  def conforms[A]: A <:< A = $conforms[A]  
  /** An instance of `A =:= B` witnesses that the types `A` and `B` are equal.  
   *  
   * @see `<:<` for expressing subtyping constraints  
   */  

是定義在scala.Predef裡面, 可參考source code

source code有點複雜, 可以參考簡化版本
 sealed abstract class <:<[-From, +To] extends  
  (From => To) with Serializable  
 implicit def conforms[A]: A <:< A = new (A <:< A) {  
  def apply(x: A) = x  
 }  


直白上來說C <:< Traversable[A]算是syntax sugar
原本應該是 <:<[C , Traversable[A] ]

那麼<:<定義裡面透過variance 來實現subtype constraints
type <:<[-From, +To]定義上是透過variance trick 來驗證subtype relationship
再去注意到
implicit def conforms[A]: A <:< A = new (A <:< A) {
  def apply(x: A) = x
}

Trick point:

因為conform接受 A <:< A (也就是 <:<[A, A])
那麼在<:<[-From, +To] 定義中明確表示 -From是 Contravariant
可接受super type variance
所以只要  B <:  A, 就 <:<[B, A] 可以轉換成conform的 <:<[A, A]
以上的推論都是compiler 在type system 的推導
因此要構成 <:<[B, A]這個type, 就是要確保 B 是A的 subtype

所以<:< 不是 operator
是一個type, 透過type variance, 來實作subtype的語意

然後再加上syntax sugar 
<:<[B, A] => B <:< A 

才達到這樣的目的


沒有留言:

張貼留言