Impredicative types vs. plain old subtyping

前端 未结 2 486
太阳男子
太阳男子 2020-12-12 22:01

A friend of mine posed a seemingly innocuous Scala language question last week that I didn\'t have a good answer to: whether there\'s an easy way to declare a collection of

2条回答
  •  长情又很酷
    2020-12-12 22:10

    @hammar's answer is perfectly right. Here is the scala way of doint it. For the example i'll take Show as the type class and the values i and d to pack in a list :

    // The type class
    trait Show[A] {
       def show(a : A) : String
    }
    
    // Syntactic sugar for Show
    implicit final class ShowOps[A](val self : A)(implicit A : Show[A]) {
      def show = A.show(self)
    }
    
    implicit val intShow    = new Show[Int] {
      def show(i : Int) = "Show of int " + i.toString
    }
    
    implicit val stringShow = new Show[String] {
      def show(s : String) = "Show of String " + s
    }
    
    
    val i : Int    = 5
    val s : String = "abc"
    

    What we want is to be able run the following code

    val list = List(i, s)
    for (e <- list) yield e.show
    

    Building the list is easy but the list won't "remember" the exact type of each of its elements. Instead it will upcast each element to a common super type T. The more precise super super type between String and Int being Any, the type of the list is List[Any].

    The problem is: what to forget and what to remember? We want to forget the exact type of the elements BUT we want to remember that they are all instances of Show. The following class does exactly that

    abstract class Ex[TC[_]] {
      type t
      val  value : t
      implicit val instance : TC[t]
    }
    
    implicit def ex[TC[_], A](a : A)(implicit A : TC[A]) = new Ex[TC] {
      type t = A
      val  value    = a
      val  instance = A
    }
    

    This is an encoding of the existential :

    val ex_i : Ex[Show] = ex[Show, Int](i)
    val ex_s : Ex[Show] = ex[Show, String](s)
    

    It pack a value with the corresponding type class instance.

    Finally we can add an instance for Ex[Show]

    implicit val exShow = new Show[Ex[Show]] {
      def show(e : Ex[Show]) : String = {
        import e._
        e.value.show 
      }
    }
    

    The import e._ is required to bring the instance into scope. Thanks to the magic of implicits:

    val list = List[Ex[Show]](i , s)
    for (e <- list) yield e.show
    

    which is very close to the expected code.

提交回复
热议问题