Scala 方法和更高级的类型参数
Scala methods and higher-kinded type parameters
我正在尝试在 Scala 中定义一个方法,该方法采用 S[_] <: Seq[Double]
的泛型类型和 returns S[FixedLoad](FixedLoad 是具体类型)。但是我的实现给了我错误,我不知道为什么。尽管我已经尝试了很多次来理解参数类型和更高种类的类型,但我的知识增长如此缓慢。
我想要实现的是不丢失 S 的具体类型(序列子类型)。
代码如下:
import scala.collection.generic.CanBuildFrom
class FixedLoad(val id: Int, val positionInT: Int, val amplitude: Double) {
override def toString: String = s"FixedLoad($id, $positionInT, $amplitude)"
}
object Load {
implicit def toFixedLoads[S[_] <: Seq[Double]](l: S[Double])(implicit cbf: CanBuildFrom[Nothing, FixedLoad, S[FixedLoad]]): S[FixedLoad] = {
l.map(_ => new FixedLoad(1, 1, 1)).to[S]
}
def main(args: Array[String]): Unit = {
println(toFixedLoads(List(1.0, 2.0, 3.0)))
}
}
和错误:
Error:(16, 13) inferred type arguments [List] do not conform to method toFixedLoads's type parameter bounds [S[_] <: Seq[Double]]
println(toFixedLoads(List(1.0, 2.0, 3.0)))
Error:(16, 30) type mismatch;
found : List[Double]
required: S[Double]
println(toFixedLoads(List(1.0, 2.0, 3.0)))
简答:
将toFixedLoads[S[_] <: Seq[Double]]
更改为toFixedLoads[S[A] <: Seq[A]]
长答案:
当你说 S[_]
时,那是 更高等的类型 。或者,换句话说,它是一个 类型的构造函数 。这意味着它需要一个类型来产生最终的 正确的类型 。以下是一些示例:
List
- 采用类型,例如Int
,生成正确的类型 List[Int]
Option
- 采用类型,例如Int
,生成正确的类型 Option[Int]
等等
这种类型的构造函数通常表示为 * -> *
。您提供一种类型,然后返回一种类型。还有其他种类;例如,Map
和 Either
需要两个类型来生成正确的类型(例如 Map[Int, String]
或 Either[Error, Foo]
),因此它们的种类是 * -> * -> *
。将其视为柯里化类型构造函数;接受一个类型和 returns 一个接受类型的函数,然后你得到最终的、正确的类型。或者换句话说,采用两种类型来生成最终的正确类型。您可能还有一个类型构造函数,它需要一个类型构造函数来构建正确的类型(例如 Monad[F[_]]
),在这种情况下,种类是 (* -> *) -> *
(例如 List -> Monad[List]
)。
所以当你说你的方法需要一个 S[Double]
类型的参数并且你传递 List(1.0, 2.0, 3.0)
时,编译器推断 S
为 List
,并且它抱怨List[A]
不是任何 A
的 Seq[Double]
的子类型。您修复此问题的第一次尝试可能是 F[_] <: Seq[_]
,但无法编译,因为内部类型仍然不对齐。我们需要用 F[A] <: Seq[A] for some A
来 "connect" 它们,可以简单地写成 F[A] <: Seq[A]
.
一个好问题可能是 "can I say S <: Seq[Double]
?" 当然,S
代表一个正确的类型,所以你完全可以!这样的东西很好用:
def foo[S <: Seq[Double]](s: S) = println(s)
foo(List(1.0, 2.0)) // prints List(1.0, 2.0)
当然,你的S
里面有一个"hole",因为你的方法参数是S[Double]
类型的,所以它不适用于你的情况。
我正在尝试在 Scala 中定义一个方法,该方法采用 S[_] <: Seq[Double]
的泛型类型和 returns S[FixedLoad](FixedLoad 是具体类型)。但是我的实现给了我错误,我不知道为什么。尽管我已经尝试了很多次来理解参数类型和更高种类的类型,但我的知识增长如此缓慢。
我想要实现的是不丢失 S 的具体类型(序列子类型)。
代码如下:
import scala.collection.generic.CanBuildFrom
class FixedLoad(val id: Int, val positionInT: Int, val amplitude: Double) {
override def toString: String = s"FixedLoad($id, $positionInT, $amplitude)"
}
object Load {
implicit def toFixedLoads[S[_] <: Seq[Double]](l: S[Double])(implicit cbf: CanBuildFrom[Nothing, FixedLoad, S[FixedLoad]]): S[FixedLoad] = {
l.map(_ => new FixedLoad(1, 1, 1)).to[S]
}
def main(args: Array[String]): Unit = {
println(toFixedLoads(List(1.0, 2.0, 3.0)))
}
}
和错误:
Error:(16, 13) inferred type arguments [List] do not conform to method toFixedLoads's type parameter bounds [S[_] <: Seq[Double]]
println(toFixedLoads(List(1.0, 2.0, 3.0)))
Error:(16, 30) type mismatch;
found : List[Double]
required: S[Double]
println(toFixedLoads(List(1.0, 2.0, 3.0)))
简答:
将toFixedLoads[S[_] <: Seq[Double]]
更改为toFixedLoads[S[A] <: Seq[A]]
长答案:
当你说 S[_]
时,那是 更高等的类型 。或者,换句话说,它是一个 类型的构造函数 。这意味着它需要一个类型来产生最终的 正确的类型 。以下是一些示例:
List
- 采用类型,例如Int
,生成正确的类型List[Int]
Option
- 采用类型,例如Int
,生成正确的类型Option[Int]
等等
这种类型的构造函数通常表示为 * -> *
。您提供一种类型,然后返回一种类型。还有其他种类;例如,Map
和 Either
需要两个类型来生成正确的类型(例如 Map[Int, String]
或 Either[Error, Foo]
),因此它们的种类是 * -> * -> *
。将其视为柯里化类型构造函数;接受一个类型和 returns 一个接受类型的函数,然后你得到最终的、正确的类型。或者换句话说,采用两种类型来生成最终的正确类型。您可能还有一个类型构造函数,它需要一个类型构造函数来构建正确的类型(例如 Monad[F[_]]
),在这种情况下,种类是 (* -> *) -> *
(例如 List -> Monad[List]
)。
所以当你说你的方法需要一个 S[Double]
类型的参数并且你传递 List(1.0, 2.0, 3.0)
时,编译器推断 S
为 List
,并且它抱怨List[A]
不是任何 A
的 Seq[Double]
的子类型。您修复此问题的第一次尝试可能是 F[_] <: Seq[_]
,但无法编译,因为内部类型仍然不对齐。我们需要用 F[A] <: Seq[A] for some A
来 "connect" 它们,可以简单地写成 F[A] <: Seq[A]
.
一个好问题可能是 "can I say S <: Seq[Double]
?" 当然,S
代表一个正确的类型,所以你完全可以!这样的东西很好用:
def foo[S <: Seq[Double]](s: S) = println(s)
foo(List(1.0, 2.0)) // prints List(1.0, 2.0)
当然,你的S
里面有一个"hole",因为你的方法参数是S[Double]
类型的,所以它不适用于你的情况。