为什么 for comprehension 扩展为 `withFilter`
Why does a for comprehension expand to a `withFilter`
我正在为关系(SQL 类)运算符开发 DSL。我有一个 Rep[Table]
类型和 .apply: ((Symbol, ...)) => Obj
方法,returns 一个对象 Obj
定义了 .flatMap: T1 => T2
和 .map: T1 => T3
函数。由于类型 Rep[Table]
对底层 table 的模式一无所知,因此 apply
方法就像一个投影 - 仅投影参数元组中指定的字段(很像untyped scalding api)。现在类型 T1
是一个 "tuple-like",它的长度被一些无形的魔法限制为投影元组的长度,但是元组元素的类型由 api 用户决定,所以代码如下
val a = loadTable(...)
val res = a(('x, 'y)).map { (t: Row2[Int, Int]) =>
(1, t(0))
}
或
val res = a(('x, 'y)).map { (t: Row2[String, String]) =>
(1, t(0))
}
工作正常。请注意,必须明确指定 map
/flatMap
函数的参数类型。但是,当我尝试将它与 for comprehension
一起使用时
val a = loadTable(...)
val b = loadTable(...)
val c = loadTable(...)
val res = for {
as: Row2[Int, Int] <- a(('ax, 'ay))
bs: Row2[Int, Int] <- b(('bx, 'by))
cs: Row2[Int, Int] <- c(('cx, 'cy))
} yield (as(0), as(1), bs(0), bs(1), cs(0), cs(1))
它 抱怨缺少 withFilter
运算符。添加 .withFilter: T1 => Boolean
不会削减它 - 然后它会抱怨 "missing parameter type for expanded function",因为 T1
是由某种类型参数化的。仅添加 .withFilter: Row[Int, Int] => Boolean
即可,但显然根本不是我想要的。
我的问题是:为什么首先调用 withFilter
以及如何将它与我的参数化类元组类型 T1
一起使用?
编辑
最后我选择了一个 .withFilter: NothingLike => BoolLike
,这是一个简单的检查,比如 _.isInstanceOf[T1]
和一个更受限制的 .filter: T1 => BoolLike
在一般情况下使用。
不幸的是,当您希望根据 lambda 的参数类型进行类型推断时,您不能使用 for-comprehensions。
实际上,在您的示例中,as: Row2[Int, Int]
被解释为模式匹配:
val res = for {
as: Row2[Int, Int] <- a(('ax, 'ay))
} yield (...)
翻译成类似:
a(('ax, 'ay)).withFilter(_.isInstanceOf[Row2[Int, Int]]).map(...)
for comprehensions 中的模式匹配非常有用:
val names = for {
("name", name) <- keyValPairs
} yield name
但权衡是,您不能明确指定 lambda 的参数类型。
我也遇到了这个问题。感谢 gzm0 对 Scala 编译器行为的解释,我想到了这个解决方法:
import cats._
import cats.data._
import cats.implicits._
object CatsNEL extends App {
val nss: NonEmptyList[(Int, String)] = NonEmptyList.of((1,"a"), (2, "b"), (3, "c"))
val ss: NonEmptyList[String] = for {
tuple <- nss
(n, s) = tuple
} yield s
}
我正在为关系(SQL 类)运算符开发 DSL。我有一个 Rep[Table]
类型和 .apply: ((Symbol, ...)) => Obj
方法,returns 一个对象 Obj
定义了 .flatMap: T1 => T2
和 .map: T1 => T3
函数。由于类型 Rep[Table]
对底层 table 的模式一无所知,因此 apply
方法就像一个投影 - 仅投影参数元组中指定的字段(很像untyped scalding api)。现在类型 T1
是一个 "tuple-like",它的长度被一些无形的魔法限制为投影元组的长度,但是元组元素的类型由 api 用户决定,所以代码如下
val a = loadTable(...)
val res = a(('x, 'y)).map { (t: Row2[Int, Int]) =>
(1, t(0))
}
或
val res = a(('x, 'y)).map { (t: Row2[String, String]) =>
(1, t(0))
}
工作正常。请注意,必须明确指定 map
/flatMap
函数的参数类型。但是,当我尝试将它与 for comprehension
val a = loadTable(...)
val b = loadTable(...)
val c = loadTable(...)
val res = for {
as: Row2[Int, Int] <- a(('ax, 'ay))
bs: Row2[Int, Int] <- b(('bx, 'by))
cs: Row2[Int, Int] <- c(('cx, 'cy))
} yield (as(0), as(1), bs(0), bs(1), cs(0), cs(1))
它 抱怨缺少 withFilter
运算符。添加 .withFilter: T1 => Boolean
不会削减它 - 然后它会抱怨 "missing parameter type for expanded function",因为 T1
是由某种类型参数化的。仅添加 .withFilter: Row[Int, Int] => Boolean
即可,但显然根本不是我想要的。
我的问题是:为什么首先调用 withFilter
以及如何将它与我的参数化类元组类型 T1
一起使用?
编辑
最后我选择了一个 .withFilter: NothingLike => BoolLike
,这是一个简单的检查,比如 _.isInstanceOf[T1]
和一个更受限制的 .filter: T1 => BoolLike
在一般情况下使用。
不幸的是,当您希望根据 lambda 的参数类型进行类型推断时,您不能使用 for-comprehensions。
实际上,在您的示例中,as: Row2[Int, Int]
被解释为模式匹配:
val res = for {
as: Row2[Int, Int] <- a(('ax, 'ay))
} yield (...)
翻译成类似:
a(('ax, 'ay)).withFilter(_.isInstanceOf[Row2[Int, Int]]).map(...)
for comprehensions 中的模式匹配非常有用:
val names = for {
("name", name) <- keyValPairs
} yield name
但权衡是,您不能明确指定 lambda 的参数类型。
我也遇到了这个问题。感谢 gzm0 对 Scala 编译器行为的解释,我想到了这个解决方法:
import cats._
import cats.data._
import cats.implicits._
object CatsNEL extends App {
val nss: NonEmptyList[(Int, String)] = NonEmptyList.of((1,"a"), (2, "b"), (3, "c"))
val ss: NonEmptyList[String] = for {
tuple <- nss
(n, s) = tuple
} yield s
}