代码之家  ›  专栏  ›  技术社区  ›  steinybot

如何在Scala中将路径相关类型与类型类一起使用

  •  4
  • steinybot  · 技术社区  · 6 年前

    Foo 使用抽象类型成员 F . 例如 Bar 将提供混凝土类型。

    然后是一个类型类 Baz . 我为每个具体类型的 Foo#F (但不适用于

    sealed trait Foo {
      type F
    }
    
    object Bar extends Foo {
      type F = Array[Byte]
    }
    
    trait Baz[B] {
      def b(b: B): String
    }
    
    object Baz {
      implicit val bazByteArray: Baz[Array[Byte]] = (b: Array[Byte]) => new String(b)
    }
    

    我无法编译:

    def f(a: Foo): Baz[a.F] = {
      val baz = a match {
        case bar@Bar => g(bar)
      }
      baz
    } // Expression of type Baz[(a.type with Bar.type)#F] doesn't conform to Baz[a.F]
    
    val x2: Foo = Bar
    val y2: Baz[x2.F] = f(x2) // Expression of type Baz[Foo#F] doesn't conform to expected type Baz[x2.F]
    

    这会编译:

    def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
      baz
    }
    
    val x1: Bar.type = Bar
    val y1: Baz[x1.F] = f(x1)
    

    为什么会这样 g 编译但不编译 f

    f

    1 回复  |  直到 6 年前
        1
  •  4
  •   Andrey Tyukin    6 年前

    似乎有点相似 to this question . 下面是一种使其编译的方法:

    sealed trait Foo {
      type F
      def asSingleton: FooSingleton[F]
    }
    
    trait FooSingleton[X] extends Foo {
      type F = X
      def asSingleton: FooSingleton[X] = this
    }
    
    object Bar extends FooSingleton[Array[Byte]]
    
    trait Baz[B] {
      def b(b: B): String
    }
    
    object Baz {
      implicit val bazByteArray: Baz[Array[Byte]] = 
        (b: Array[Byte]) => new String(b)
    }
    
    def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
      baz
    }
    
    val x1: Bar.type = Bar
    val y1: Baz[x1.F] = f(x1)
    
    def f[T](a: Foo { type F = T } ): Baz[T] = {
      (a.asSingleton: FooSingleton[T]) match {
        case bar @ Bar => g(bar)
      }
    }
    
    val x2: Foo = Bar
    val y2: Baz[x2.F] = f(x2)
    

    你的 g baz 类型 Baz[a.F] 从外面 ,编译器插入一个具体的隐式实例,实际值 a 里面什么地方都不用 .

    你的 f B[a.F] 出现 只有 传递给 .

    从某种意义上说 以及返回值,因为它会进行以下“不连续跳跃”:

    • 从…开始 a: Foo
    • 跳出 Bar
    • 使用 从混凝土中取出 酒吧 Baz[Array[Byte]]
    • 把这个还给我 Baz[数组[字节]] 巴兹 .

    这个路径可以通过证明不连续的“跳跃”确实只是一个一直停留在同一地点的身份路径来修复,所以它真的不会移动到任何地方,所以 a.F 而推断的类型是相同的,即 T .