了解 seq[AnyVal] 和 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])

但这行不通。

有没有办法优雅地对类型施加我所需的约束?

注意上限之间的差异

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

我觉得你应该为两者编写一个单独的函数,但还有其他方法可以做到这一点:

在 Dotty 中,你可以只使用联合类型,这是我在这里推荐的:

编辑:根据阿列克谢·罗曼诺夫的建议,将Seq[AnyVal | String]替换为Seq[AnyVal | String],这是错误的,

def foo(seq: Seq[AnyVal] | Seq[String]): Unit = {
println(seq)
}

斯卡斯蒂


如果你没有使用Dotty,你仍然可以在Scala 2中使用几个可重用的implicit def来做到这一点


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

斯卡斯蒂


你也可以用 typeclass 来做,正如 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))

在斯卡斯蒂

def f1( seq: Seq[Any] ): Unit = 
println( seq( 0 ) )

这是有效的,因为Int 和 String 最不常见的超类型(作为两者超类型的"最低"类型(将是Any,因为 Int 是AnyVal的子类型(即"基元"(和字符串是AnyRef的子类型(即"堆对象"(。f1不以任何方式依赖于列表的内容,并且其返回类型 (Unit( 中不是多态的,所以我们根本不需要类型参数。

最新更新