理解 Seq[AnyVal] 和 Seq[String] 的混合上下文边界
Understanding Mixed Context Bounds of Seq[AnyVal] and Seq[String]
假设我有一些函数需要一个整数序列或一个字符串序列。
我的尝试:
object Example extends App {
import scala.util.Random
val rand: Random.type = scala.util.Random
// raw data
val x = Seq(1, 2, 3, 4, 5).map(e => e + rand.nextDouble())
val y = Seq("chc", "asas")
def f1[T <: AnyVal](seq: Seq[T]) = {
println(seq(0))
}
// this works fine as expected
f1(x)
// how can i combine
f1(y)
}
我怎样才能添加它来处理字符串?
如果我将方法签名更改为:
def f1[T <: AnyVal:String](seq: Seq[T])
但这行不通。
有没有办法优雅地对类型施加我需要的约束?
def f1( seq: Seq[Any] ): Unit =
println( seq( 0 ) )
这是有效的,因为 Int 和 String 的最不常见的超类型('lowest' 类型是两者的超类型)将是 Any
,因为 Int 是 [=12= 的子类型](即“原语”)和 String 是 AnyRef
(即“堆对象”)的子类型。 f1
不以任何方式依赖于列表的内容,并且在其 return 类型 (Unit
) 中不是多态的,因此我们根本不需要类型参数。
我觉得你应该为两者编写一个单独的函数,但还有其他方法可以做到:
在 Dotty 中,你可以只使用联合类型,这是我在这里推荐的:
编辑:根据 Alexey Romanov 的建议,将 Seq[AnyVal | String]
替换为 Seq[AnyVal | String]
,这是错误的,
def foo(seq: Seq[AnyVal] | Seq[String]): Unit = {
println(seq)
}
如果您不使用 Dotty,您仍然可以在 Scala 2 中使用一些可重复使用的 implicit def
s
class Or[A, B]
implicit def orA[A, B](implicit ev: A): Or[A, B] = new Or
implicit def orB[A, B](implicit ev: B): Or[A, B] = new Or
def foo[T](seq: Seq[T])(implicit ev: Or[T <:< AnyVal, T =:= String]): Unit =
println(seq)
foo(Seq("baz", "waldo"))
foo(Seq(23, 34))
foo(Seq(List(), List())) //This last one fails
正如 Luis Miguel Mejía Suárez 所建议的那样,您也可以使用类型类来完成它,但我不建议将其用于这种微不足道的任务,因为您仍然必须为每种类型定义 2 个函数以及一个特征, 2 个隐式对象和一个可以使用该特征实例的函数。您可能不希望此模式只处理 2 种不同的类型。
sealed trait DoSomethingToIntOrString[T] {
def doSomething(t: Seq[T]): Unit
}
implicit object DoSomethingToAnyVal extends DoSomethingToAnyValOrString[AnyVal] {
def doSomething(is: Seq[AnyVal]): Unit = println(is)
}
implicit object DoSomethingToString extends DoSomethingToIntOrString[String] {
def doSomething(ss: Seq[String]): Unit = println(ss)
}
def foo[T](t: Seq[T])(implicit dsis: DoSomethingToIntOrString[T]): Unit = {
dsis.doSomething(t)
}
foo(Seq("foo", "bar"))
foo(Seq(1, 2))
注意 上限
之间的区别
A <: C
A : C
所以类型参数子句[T <: AnyVal : String]
没有多大意义。 String
等类型也很少(或从不)用作上下文边界。
这是一个类型类方法
trait EitherStringOrAnyVal[T]
object EitherStringOrAnyVal {
implicit val str: EitherStringOrAnyVal[String] = new EitherStringOrAnyVal[String] {}
implicit def aval[T <: AnyVal]: EitherStringOrAnyVal[T] = new EitherStringOrAnyVal[T] {}
}
def f1[T: EitherStringOrAnyVal](seq: Seq[T]): Unit = {
println(seq(0))
}
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
或泛化类型约束方法
object Foo {
private def impl[T](seq: Seq[T]): Unit = {
println(seq(0))
}
def f1[T](seq: Seq[T])(implicit ev: T =:= String): Unit = impl(seq)
def f1[T <: AnyVal](seq: Seq[T]): Unit = impl(seq)
}
import Foo._
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
假设我有一些函数需要一个整数序列或一个字符串序列。
我的尝试:
object Example extends App {
import scala.util.Random
val rand: Random.type = scala.util.Random
// raw data
val x = Seq(1, 2, 3, 4, 5).map(e => e + rand.nextDouble())
val y = Seq("chc", "asas")
def f1[T <: AnyVal](seq: Seq[T]) = {
println(seq(0))
}
// this works fine as expected
f1(x)
// how can i combine
f1(y)
}
我怎样才能添加它来处理字符串?
如果我将方法签名更改为:
def f1[T <: AnyVal:String](seq: Seq[T])
但这行不通。
有没有办法优雅地对类型施加我需要的约束?
def f1( seq: Seq[Any] ): Unit =
println( seq( 0 ) )
这是有效的,因为 Int 和 String 的最不常见的超类型('lowest' 类型是两者的超类型)将是 Any
,因为 Int 是 [=12= 的子类型](即“原语”)和 String 是 AnyRef
(即“堆对象”)的子类型。 f1
不以任何方式依赖于列表的内容,并且在其 return 类型 (Unit
) 中不是多态的,因此我们根本不需要类型参数。
我觉得你应该为两者编写一个单独的函数,但还有其他方法可以做到:
在 Dotty 中,你可以只使用联合类型,这是我在这里推荐的:
编辑:根据 Alexey Romanov 的建议,将 Seq[AnyVal | String]
替换为 Seq[AnyVal | String]
,这是错误的,
def foo(seq: Seq[AnyVal] | Seq[String]): Unit = {
println(seq)
}
如果您不使用 Dotty,您仍然可以在 Scala 2 中使用一些可重复使用的 implicit def
s
class Or[A, B]
implicit def orA[A, B](implicit ev: A): Or[A, B] = new Or
implicit def orB[A, B](implicit ev: B): Or[A, B] = new Or
def foo[T](seq: Seq[T])(implicit ev: Or[T <:< AnyVal, T =:= String]): Unit =
println(seq)
foo(Seq("baz", "waldo"))
foo(Seq(23, 34))
foo(Seq(List(), List())) //This last one fails
正如 Luis Miguel Mejía Suárez 所建议的那样,您也可以使用类型类来完成它,但我不建议将其用于这种微不足道的任务,因为您仍然必须为每种类型定义 2 个函数以及一个特征, 2 个隐式对象和一个可以使用该特征实例的函数。您可能不希望此模式只处理 2 种不同的类型。
sealed trait DoSomethingToIntOrString[T] {
def doSomething(t: Seq[T]): Unit
}
implicit object DoSomethingToAnyVal extends DoSomethingToAnyValOrString[AnyVal] {
def doSomething(is: Seq[AnyVal]): Unit = println(is)
}
implicit object DoSomethingToString extends DoSomethingToIntOrString[String] {
def doSomething(ss: Seq[String]): Unit = println(ss)
}
def foo[T](t: Seq[T])(implicit dsis: DoSomethingToIntOrString[T]): Unit = {
dsis.doSomething(t)
}
foo(Seq("foo", "bar"))
foo(Seq(1, 2))
注意 上限
之间的区别A <: C
A : C
所以类型参数子句[T <: AnyVal : String]
没有多大意义。 String
等类型也很少(或从不)用作上下文边界。
这是一个类型类方法
trait EitherStringOrAnyVal[T]
object EitherStringOrAnyVal {
implicit val str: EitherStringOrAnyVal[String] = new EitherStringOrAnyVal[String] {}
implicit def aval[T <: AnyVal]: EitherStringOrAnyVal[T] = new EitherStringOrAnyVal[T] {}
}
def f1[T: EitherStringOrAnyVal](seq: Seq[T]): Unit = {
println(seq(0))
}
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
或泛化类型约束方法
object Foo {
private def impl[T](seq: Seq[T]): Unit = {
println(seq(0))
}
def f1[T](seq: Seq[T])(implicit ev: T =:= String): Unit = impl(seq)
def f1[T <: AnyVal](seq: Seq[T]): Unit = impl(seq)
}
import Foo._
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok