当使用中间变量时,理解失败

时间:2018-02-26 15:52:39

标签: scala pattern-matching

Scastie version

有了这种基础结构:

trait Pat[A]
object Pat {
  def apply[A](elems: A*): Pat[A] = ???
}

implicit class PatOps[A](p: Pat[A]) {
  def ++ (that: Pat[A]): Pat[A] = ???

  def bubble: Pat[Pat[A]] = ???

  def grouped(size: Pat[Int]): Pat[Pat[A]] = ???
}

implicit class PatPatOps[A](p: Pat[Pat[A]]) {
  def map[B](f: Pat[A] => Pat[B]): Pat[Pat[B]] = ???

  def flatMap[B](f: Pat[A] => Pat[B]): Pat[B] = ???

  def flatten: Pat[A] = ???
}

可以编写以下for-comprehension:

trait Test1 {
  val lPat = Pat(1, 2, 3)
  val xs = for {
    len     <- lPat.bubble
    cantus  <- Pat(4, 40, 3).grouped(len)
  } yield {
    cantus ++ Pat(-1)
  }
  xs.flatten
}

但是这个使用中间变量失败了:

trait Test2 {
  val lPat = Pat(1, 2, 3)
  val xs = for {
    len     <- lPat.bubble  // XXX
    brown = Pat(4, 40, 3)
    cantus  <- brown.grouped(len)
  } yield {
    cantus ++ Pat(-1)
  }
  xs.flatten
}

标记为XXX的行的错误是:

type mismatch;
 found   : (Playground.this.Pat[Int], Playground.this.Pat[Int])
 required: Playground.this.Pat[?]

Scala是2.12.4

1 个答案:

答案 0 :(得分:8)

当您使用过度限制性签名map定义map[B](f: Pat[A] => Pat[B])时会发生这种情况。回想一下,通常,它应该接受具有任意结果类型B的函数,也就是说,它应该是这样的:

map[B](f: A => B): <stuff>

现在,您对中间辅助变量brown

的理解
  val xs = for {
    len     <- lPat.bubble
    brown = Pat(4, 40, 3)
    cantus  <- brown.grouped(len)
  } yield {
    cantus ++ Pat(-1)
  }
使用map

重写为

  val xs = lPat.bubble.map(((len) => {
    val brown = Pat(4, 40, 3);
    scala.Tuple2(len, brown)
  })).flatMap(((x$1) => x$1: @scala.unchecked match {
    case scala.Tuple2((len @ _), (brown @ _)) => 
      brown.
        grouped(len).
        map(((cantus) => cantus.$plus$plus(Pat(-1))))
  }))

,如the documentationin my overly detailed answer here中所述。

请注意隐式生成的map的返回类型现在类似于(Pat[A], Pat[Int])(元组(len, brown)的类型),并且与模式{{不匹配1}}来自你的声明。

我没有看到任何变通方法。尽你所能避免将Pat[B]定义为map,否则它会表现得太奇怪了。避免破坏map[B](f: Pat[A] => Pat[B])的兼容性。如果map无法Pat[X] map f: X => YPat[Y]X进行Y,请不要将其称为map }}

编辑:总有一种解决方法......

您可以做的一件事是介绍某种隐式提供的CanPatFrom

trait CanPatFrom[X, A] extends (X => Pat[A])

然后

...
def map[X, B](f: Pat[A] => X)(implicit cpf: CanPatFrom[X, B]) = {
  val pb: Pat[B] = cpf(f(...))
  /* do your stuff here with `Pat[B]` instead of 
   * generic `X`
   */
  ...
}

假设你的Pat带有某种笛卡尔 - 幺半群结构,你可以定义

  • CanPatFrom[Pat[A], Pat[A]]
  • CanPatFrom[(Pat[A], Pat[B]), Pat[(A, B)]]
  • CanPatFrom[(Pat[A], Pat[B], Pat[C]), Pat[(A, B, C)]]
  • ...

从而获得一个map,它至少可以应对返回类型为元组的情况。