约束接受

时间:2017-04-05 15:35:52

标签: scala casting either

我正在尝试制作这样的函数:

 def foo(x: Either[String, Int]) = x match {
   case Left(s) => Left("foo:" + s)
   case Right(n) => Right(n+1)
 }

这样可行,但我正在寻找一种方法来确保调用者,结果将始终与输入的类型相同 - 如果输入为Left,则返回Left,如果是Right,则右转。

有人可以想到一个可以用来做那个的巧妙技巧吗?

我知道,我可以这样做:

 def foo[T <: Either[String, Int]](x: T): T = (x match {
   case Left(s) => Left("foo:" + s)
   case Right(n) => Right(n+1)
 }).asInstanceOf[T]

...但是演员到底是丑陋的:( 这个声明将成为一个基本特征的抽象成员,即几个实现&#34;插件&#34;将需要覆盖,我不想让他们所有人都必须做这种类型铸造的事情。

我还可以创建两个单独的函数fooStringfooInt ...但这是我想要避免的,因为某些考虑因素,特定于我特定的api继续努力。

还有其他想法吗?

2 个答案:

答案 0 :(得分:2)

如果您不限于使用Either,则可以使用类型类 -

sealed trait Transformer[A] {
 def transform(n: A): A
}
object Transformer {
 implicit object IntTransformer extends Transformer[Int] { def transform(n: Int) = n + 1 }
 implicit object StringTransformer extends Transformer[String] { def transform(s: String) = "foo:" + s }
}

def foo[A: Transformer](x: A)(implicit transformer: Transformer[A]) = transformer.transform(x)

答案 1 :(得分:1)

这个签名实际上并没有说出你想说的话:

val x = Left("a")
val y = foo[x.type](x)

y的类型为x.type,因此它必须是同一个实例,而不是。因此,如果您想避免强制转换,则需要更改签名。一种方法(未经测试):

trait LowPriorityFooImplicits { _: FooImplicits =>
  implicit def eitherFoo(x: Either[String, Int]): Foo[Either[String, Int]] = new Foo(x) {
    def foo() = x match {
      case y: Left[String, Int] => y.foo()
      case z: Right[String, Int] => z.foo()
  }
}

trait FooImplicits extends LowPriorityFooImplicits {
  sealed trait Foo[A <: Either[String, Int]](x: A) {
    def foo(): A
  }

  implicit def leftFoo(x: Left[String, Int]): Foo[Left[String, Int]] = new Foo(x) {
    def foo() = Left(fooString(x.value))
  }
  implicit def rightFoo ...

  // x will be implicitly converted from a subtype of Either[String, Int]
  def foo[A](x: Foo[A]): T = x.foo()

  protected def fooString(s: String) = "foo:" + s
  protected def fooInt(n: Int) = n + 1
}

(它仍然有fooStringfooInt,但不在公共API中。)