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] = ???
}
可以编写以下理解:
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的行的错误是:
Scala是2.12.4
最佳答案
当您使用过于严格的签名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 documentation或in my overly detailed answer here中所述。
注意,隐式生成的
map
的返回类型现在类似于(Pat[A], Pat[Int])
(元组(len, brown)
的类型),并且与声明中的Pat[B]
模式不匹配。我没有任何解决方法。请尽一切可能避免将
map
定义为map[B](f: Pat[A] => Pat[B])
,否则它的行为会太奇怪了。避免破坏map
的功能。如果您的Pat[X]
无法将map
的f: X => Y
转换为任意Pat[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
带有某种笛卡尔-monoidal结构,则可以定义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
,它至少可以处理返回类型为元组的情况。