为什么在Scala 2.12中不再编译带有模式匹配和更高kinded类型的代码片段?

时间:2018-03-20 01:51:32

标签: scala pattern-matching higher-kinded-types

我正在编写一段看起来类似于以下内容的代码(Scastie for your convenience):

import scala.language.higherKinds

sealed trait Wrapping
sealed trait PinkWrap extends Wrapping
sealed trait GreenWrap extends Wrapping

sealed trait Foo[M[_], A] {}
case class MintFoo[M[_], A](a : A) extends Foo[M,A]
case class LiquoriceFoo[M[_], A](a : A) extends Foo[M,A]
sealed trait WrappedFoo[M[_], _, A]
case class FooInPinkWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, PinkWrap, A]
case class FooInGreenWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, GreenWrap, A]

object Utils {
   def analyzeFoo[M[_], S <: Wrapping, A](w: WrappedFoo[M, S, A]): String = {
    w match {
      case FooInPinkWrap(f: Foo[M,A]) => tasteFoo[M,A](f)+" in Pink wrapping"      
      case FooInGreenWrap(f: Foo[M,A]) => tasteFoo[M,A](f)+" in Green wrapping"
    }
  }

  def tasteFoo[M[_], A](f: Foo[M,A]) : String = 
   f match {
     case MintFoo (a) => "Mint"
     case LiquoriceFoo (a) => "Liquorice"
   }
}   

以前用Scala 2.11.7完全编译。

自从该项目受到Scala 2.12.4的影响(但事实上该问题已经在Scala 2.11.11中已经可以重现),它无法使用来自scalac的以下消息进行编译{{1 }}:

  

case FooInPinkWrap(f: Foo[M,A]) => tasteFoo[M,A]

通过在&#34;构建设置&#34;中选择Scala版本2.12.4,可以在Scastie中重现这一点。

这令我感到困惑,因为M takes no type parameters, expected: one确实是1-ary。

事实上,我遇到了与以下更简单的MWE(Scastie)相同的问题:

M

在后一段代码中用import scala.language.higherKinds sealed trait GiftWrap sealed trait PinkWrap extends GiftWrap sealed trait GreenWrap extends GiftWrap sealed trait Foo[M[_], A] {} sealed trait WrappedFoo[M[_], S, A] case class FooInPinkWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, PinkWrap, A] case class FooInGreenWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, GreenWrap, A] object Utils { def tellColor[M[_], S <: GiftWrap, A](w: WrappedFoo[M, S, A]): String = { w match { case FooInPinkWrap(f: Foo[M,A]) => "Pink" case FooInGreenWrap(f: Foo[M,A]) => "Green" } } } 替换M会导致

({type T[X] = M[X]})#T

注意pattern type is incompatible with expected type; found : Playground.this.Foo[[X],A] required: Playground.this.Foo[Any,Any] Note: [X] <: Any, but trait Foo is invariant in type M. You may wish to define M as +M instead. (SLS 4.5) Note: A <: Any, but trait Foo is invariant in type A. You may wish to define A as +A instead. (SLS 4.5) M does not take type parameters

为什么会这样?

我找不到2.11.11(次要版本)的更改日志特别有启发性。

更重要的是,如何编译旧代码

  1. 正确的方式
  2. 最小修改量方式?
  3. 感谢。

1 个答案:

答案 0 :(得分:3)

修改-通知

此帖子现在包含两部分:

  1. 几个具有大幅缩减示例的解决方法
  2. 将解决方法应用于您的代码
  3. 产生相同错误的较短示例

    那些并非完全&#34;最小&#34;例子,所以我试图进一步将它煮沸。 Foo的整个层次结构和彩色包装的整个层次结构并不真正相关。这个小例子以基本相同的方式失败:

    sealed trait Foo[M[_], P]
    case class Bar[M[_]]() extends Foo[M, Int]
    
    def f[M[_], S](x: Foo[M, S]): Int = x match {
      case Bar() => g[M]
    }
    
    def g[M[_]] = 0
    

    用2.12.4编译时,它给出:

    error: M takes no type parameters, expected: one
      case Bar() => g[M]
                      ^
    one error found
    

    这里唯一有趣的是P的类型参数Foo,它被Int中的具体Bar取代,但又被制作成通用{{1}在S的签名中。

    在阅读relevant parts of documentation并查看similar, possibly related issues之后,我认为我可能不应该过多地推测为什么这在2.11中起作用,然后停止工作,现在没有&#39 ;工作在2.12。

    相反,我将简单列出几种解决方法,希望您可以找到其中一种有用的方法。

    1。您可以将构造函数转换为类型模式:

    f

    2. 您可以将构造函数值匹配转换为类型模式 变量(是的,Scala确实支持sealed trait Foo[M[_], P] case class Bar[M[_]]() extends Foo[M, Int] def f[M[_], S](x: Foo[M, S]): Int = x match { case b: Bar[M] => g[M] } def g[M[_]] = 0 中的类型变量 - match!),这是我很久没有注意到的引用[Odersky et人。 &#34;使用Scala编程&#34;,第一版,第275页]:

      

    您也可以使用(小写)类型变量。

    所以,你可以这样做,编译器实际上会尝试推断case的一些事情:

    m

    }

    3. 您可以将sealed trait Foo[M[_], P] case class Bar[M[_]]() extends Foo[M, Int] def f[M[_], S](x: Foo[M, S]): Int = x match { case b: Bar[m] => g[m] } def g[M[_]] = 0 的RHS移动到case的方法中:

    Bar

    4。(不是真正的解决方法,更多是观察)

    如果您将类型参数sealed trait Foo[M[_], P] case class Bar[M[_]]() extends Foo[M, Int] { def g: Int = 0 } def f[M[_], S](x: Foo[M, S]): Int = x match { case b @ Bar() => b.g } 修复为P而不是将其保留为多态,则此方法有效:

    Int

    返回您的代码

    以下是适用于您的代码的相同解决方法:

    1。类型模式的构造函数模式:

    sealed trait Foo[M[_], P]
    case class Bar[M[_]]() extends Foo[M, Int]
    
    def f[M[_]](x: Foo[M, Int]): Int = x match {
      case Bar() => g[M]
    }
    
    def g[M[_]] = 0
    

    2. :使用变量输入模式:

    import scala.language.higherKinds
    
    sealed trait Wrapping
    sealed trait PinkWrap extends Wrapping
    sealed trait GreenWrap extends Wrapping
    
    sealed trait Foo[M[_], A] {}
    case class MintFoo[M[_], A](a : A) extends Foo[M, A]
    case class LiquoriceFoo[M[_], A](a : A) extends Foo[M, A]
    sealed trait WrappedFoo[M[_], _, A]
    case class FooInPinkWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, PinkWrap, A]
    case class FooInGreenWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, GreenWrap, A]
    
    object Utils {
      def analyzeFoo[M[_], S <: Wrapping, A](w: WrappedFoo[M, S, A]): String = {
        w match {
          case f: FooInPinkWrap[M, A] => tasteFoo[M, A](f.m) + " in Pink wrapping"      
          case f: FooInGreenWrap[M, A] => tasteFoo[M, A](f.m) + " in Green wrapping"
        }
      }
    
      def tasteFoo[M[_], A](f: Foo[M,A]) : String = {
        f match {
          case MintFoo(a) => "Mint"
          case LiquoriceFoo(a) => "Liquorice"
        }
      }
    }
    

    3 :将sealed trait Wrapping sealed trait PinkWrap extends Wrapping sealed trait GreenWrap extends Wrapping sealed trait Foo[M[_], A] {} case class MintFoo[M[_], A](a : A) extends Foo[M, A] case class LiquoriceFoo[M[_], A](a : A) extends Foo[M, A] sealed trait WrappedFoo[M[_], _, A] case class FooInPinkWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, PinkWrap, A] case class FooInGreenWrap[M[_], A](m: Foo[M, A]) extends WrappedFoo[M, GreenWrap, A] object Utils { def analyzeFoo[M[_], S <: Wrapping, A](w: WrappedFoo[M, S, A]): String = { w match { case f: FooInPinkWrap[m, a] => tasteFoo[m, a](f.m) + " in Pink wrapping" case f: FooInGreenWrap[m, a] => tasteFoo[m, a](f.m) + " in Green wrapping" } } def tasteFoo[M[_], A](f: Foo[M,A]) : String = { f match { case MintFoo(a) => "Mint" case LiquoriceFoo(a) => "Liquorice" } } } 移至tasteBlah

    WrappedFoo

    4 (仅限最小化示例,不适用)。

    (纯粹我的个人意见):我没有发现这种行为非常直观,也许你可以尝试将其作为一个问题提交。即使有人能找到一个很好的理由,为什么它毕竟不应该编译,但错误信息仍然非常混乱。