【问题标题】:Is scala able to infer type-constructor abstract type members?scala 能够推断类型构造函数抽象类型成员吗?
【发布时间】:2017-10-22 17:27:58
【问题描述】:

例如,我正在尝试提取隐式参数的抽象类型成员(a la Shapeless)

trait F[T] { type Out }

object F {
  type Aux[T, out] = F[T] { type Out = out }
}

def glhf[t, out](implicit f: F.Aux[t, out]): out = ???

这对于任何类型的提取(甚至是复杂的交叉隐式类型变量)都很有效。

但是,当抽象类型成员是类型构造函数而不是简单类型时,编译器无法在调用点统一类型变量。

我做了一个小测试用例,其中有一个奇怪的编译错误。编译器错误本身没有多大意义,所以我想知道这是否是编译器错误?有关错误消息的详细信息,请参阅代码示例。

使用scala-2.12.4、-Xlog-implicits 编译额外消息,甚至使用-Ypartial-unification 以防出现问题。

incubator/Main.scala:

package incubator

object wat {

  /**
   * A "type class", "implicit evidence" type, etc...
   *
   * @tparam t just for looks, and facilitate
   *  the implicit resolution scenario
   */
  trait fo[t] {
    /**
     * An abstract type member THAT IS A TYPE CONSTRUCTOR
     */
    type f[_]
  }

  //
  // Types that will be used for `fo`'s abstract type `f[_]`
  //
  trait F1[t]
  trait F2[t]
  //
  // Couple of case for type class `fo`
  //
  trait loo
  implicit object loo extends loo with fo[loo] {
    type f[t] = F1[t]
  }
  //
  trait poo
  implicit object poo extends poo with fo[poo] {
    type f[t] = F2[t]
  }

  // Double checking, this compiles
  val w0 = implicitly[ fo[loo] ]
  val w1 = implicitly[ fo[poo] ]

  /**
   * *** PROBLEM HERE ***
   * 
   * A method call, in which the abstract TYPE CONSTRUCTOR type member
   * needs to be inferred by the compiler.
   *
   * This fails to be implicitly resolved, because the compiler
   * fails to instantiate the type parameters, (probably) because
   * it is unable to infer abstract type `f`. See further below
   * for the failed invocation.
   *
   */
  def fu0[t, in[_]](t: t)(
    implicit
    fo: fo[t] { type f[a] = in[a] }
  ): String = s"Hi $t: $fo"

  // These will work fine, since we explicitly set type param `in`
  val w2 = fu0[loo, F1](loo: loo)
  val w3 = fu0[poo, F2](poo: poo)

  // *** PROBLEM HERE ***
  // The following fails to compile
  val w4 = fu0(loo: loo) // type ascription for test simplification
  val w5 = fu0(poo: poo) // type ascription for test simplification

  //
  // Error message:
  //
  // (notice the "type f has one type parameter, but type in has one"
  //  part of the error)
  //
  // [info] .../incubator/Main.scala:64:15: poo is not a valid implicit value for incubator.wat.fo[incubator.wat.poo]{type f[a] = in[a]} because:
  // [info] type parameters weren't correctly instantiated outside of the implicit tree: inferred kinds of the type arguments (incubator.wat.poo.f[t]) do not conform to the expected kinds of the type parameters (type in).
  // [info] incubator.wat.poo.f[t]'s type parameters do not match type in's expected parameters:
  // [info] type f has one type parameter, but type in has one
  // [info]   val w5 = fu0(poo: poo) // type ascription for test simplification
  // [info]               ^
  // [info] .../incubator/Main.scala:64:15: incubator.this.wat.poo is not a valid implicit value for incubator.wat.fo[incubator.wat.poo]{type f[a] = in[a]} because:
  // [info] type parameters weren't correctly instantiated outside of the implicit tree: inferred kinds of the type arguments (incubator.wat.poo.f[t]) do not conform to the expected kinds of the type parameters (type in).
  // [info] incubator.wat.poo.f[t]'s type parameters do not match type in's expected parameters:
  // [info] type f has one type parameter, but type in has one
  // [info]   val w5 = fu0(poo: poo) // type ascription for test simplification
  // [info]               ^
  // [error] .../incubator/Main.scala:64:15: could not find implicit value for parameter fo: incubator.wat.fo[incubator.wat.poo]{type f[a] = in[a]}
  // [error]   val w5 = fu0(poo: poo) // type ascription for test simplification
  // [error]               ^
  // [error] two errors found
  // [error] (compile:compileIncremental) Compilation failed
  // [error] Total time: 1 s, completed Oct 22, 2017 4:48:35 PM
  //

}

【问题讨论】:

    标签: scala type-inference type-constructor scala-2.12


    【解决方案1】:

    通过在fu0 中使用以下定义,您基本上重新制作了fo,因此编译器不知道到底发生了什么。

    implicit
    fo: fo[t] { type f[a] = in[a] }
    

    您已经定义了fo,并且通过使用该静态特征,您不必使用显式类型。

    def fu0[t, in[_]]
        (t: t)
        (implicit fo: fo[t] ): String = s"Hi $t: $fo"
    

    它适用于所提供的示例,但我认为在您的实际情况下,问题在于嵌套类型更深层次。如果我的评估是准确的,请提供更具体的测试用例,以便我们进行调查。这是一个相当有趣的话题。

    复制

    使用scalaVersion := "2.12.4" 清理设置

    package io.sosc
    
    object Main {
    
        trait fo[t] {
    
            type f[_]
        }
    
        trait F1[t]
        trait F2[t]
    
        trait loo
    
        implicit object loo extends loo with fo[loo] {
            type f[t] = F1[t]
        }
    
        trait poo
    
        implicit object poo extends poo with fo[poo] {
            type f[t] = F2[t]
        }
    
        def fu0[t, in[_]]
            (t: t)
            (implicit fo: fo[t] ): String = s"Hi $t: $fo"
    
    
        def main( args: Array[ String ] ): Unit = {
    
    
    
            val w0 = implicitly[ fo[loo] ]
            val w1 = implicitly[ fo[poo] ]
    
            val w2 = fu0[loo, F1](loo: loo)
            val w3 = fu0[poo, F2](poo: poo)
    
            println( w2 )
            println( w3 )
    
            val w4 = fu0(loo: loo)
            val w5 = fu0(poo: poo)
    
            println( w4 )
            println( w5 )
        }
    }
    

    结果:

    Hi io.sosc.Main$loo$@465ba3d7: io.sosc.Main$loo$@465ba3d7
    Hi io.sosc.Main$poo$@675b0a69: io.sosc.Main$poo$@675b0a69
    Hi io.sosc.Main$loo$@465ba3d7: io.sosc.Main$loo$@465ba3d7
    Hi io.sosc.Main$poo$@675b0a69: io.sosc.Main$poo$@675b0a69
    

    【讨论】:

    • 感谢丹尼斯的回答,但正如您所说的那样,类型参数in[_] 在方法的签名中被重用。您的解决方案只是从签名中删除类型变量。该问题需要澄清以指定此用例。再次感谢。
    • 当你做综合测试用例时在评论中标记我:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    相关资源
    最近更新 更多