scala:使用implicits为方法的每个参数支持两种不同的类型?

时间:2011-07-22 20:52:50

标签: scala

假设我有一个接受大量参数的方法,每个参数可以是String或Seq [String]。在方法内部,我希望每个参数的值只是值,如果它是一个字符串,并且如果它是Seq [String]则在Seq上的mkString“”。我意识到左/右可能在这里使用,但我不希望调用者必须包装任何args。

我使用implicits强制arg为String或Seq [String]但是我无法弄清楚如何让arg将自身减少为字符串,无论是作为标识还是作为mkString,视情况而定。理想的代码可能如下所示:

trait StrOrSeqStr[A]

implicit object Str extends StrOrSeqStr[String]
implicit object SeqStr extends StrOrSeqStr[Seq[String]]

def f[A, B](a: A, b: B)(implicit a: StrOrSeqStr[A], implicit b: StrOrSeqStr[B]) {
  val aIn: String = a
  val bIn: String = b // converts strings to themselves, seq strings as _ mkString " "
}

// both should work
f(Seq("1"), "2")
f("1", Seq("2", "3"))

有什么建议吗?谢谢!

4 个答案:

答案 0 :(得分:3)

如果我错了,请纠正我,但据我了解,您只需要隐式地将String转换为Seq[String]。这个简单的隐式转换应该这样做:

implicit def stringToSeqString(s: String) = Seq(s)

def f(a: Seq[String], b: Seq[String]) {
  val (aIn, bIn) = (a mkString " ", b mkString " ")
  // ...
}

f(Seq("1"), "2")
f("1", Seq("2", "3"))

答案 1 :(得分:3)

implicit def stringsToString(s: Seq[String]) = s mkString " "

def f(s: String *) {
   println(s mkString " ")
}

scala> f("1")
1

scala> f("1", "2")
1 2

scala> f(Seq("1", "2"))
1 2

scala> f(Seq("1"), Seq("2"))
1 2

scala> f("1", Seq("2"))
1 2

scala> f(Seq("1"), "2")
1 2

答案 2 :(得分:1)

据我所知,你试图使用上下文边界来赋予函数f一些可被视为String或可以转换为它的东西。除了我的其他答案,我想添加其他使用类型类的解决方案。它更复杂,所以我建议你使用简单的隐式转换,我在另一个答案中描述,如果它解决了你的问题。

出于演示目的,我将StrOrSeqStr重命名为Displayable并使其更通用,因此它不仅适用于StringSeq[String],还适用于任何其他类型和Seq[T]如果类型参数T也有Displayable(它实际上可以用于您想要的任何类 - 您只需提供合适的隐式)。

首先,您需要定义Displayable

trait Displayable[T] {
  def getString(target: T): String
}

object Displayable {
  implicit object StringDisplayable extends Displayable[String] {
    def getString(str: String) = str
  }

  implicit object DateDisplayable extends Displayable[Date] {
    val dateFormat = new SimpleDateFormat("dd.MM.yyyy")
    def getString(date: Date) = dateFormat format date
  }

  implicit def seqDisplayable[T: Displayable] = new Displayable[Seq[T]] {
    def getString(seq: Seq[T]) = seq map implicitly[Displayable[T]].getString mkString " "
  }
}

正如您所看到的,我收集了伴随对象中的所有相关含义,因此它们始终可用且不需要显式导入(适合将来使用)。

此处有趣的方法是seqDisplayable - 它还需要T Displayable(这也会使其递归 - 例如Seq[Seq[Date]]也会Displayable }})。我还为StringDate创建了特殊规则。正如您所看到的,它非常具有限制性,允许StringDateSeq[String]Seq[Date]Seq[Seq[String]]Seq[Seq[String]]等。你想允许所有其他对象你可以添加这样的东西:

implicit def anythingDisplayable[T] = new Displayable[T] {
  def getString(anything: T) = anything toString
}

现在来了f功能:

def f[A : Displayable, B : Displayable](a: A, b: B) {
  val aIn = implicitly[Displayable[A]].getString(a)
  val bIn = implicitly[Displayable[B]].getString(b)
  // ...
}

所以我需要证据:DisplayableA应该存在一些B,然后我使用这些String将它们转换为Displayable。如果您想进一步简化它,可以从DislplayableString的某种类型创建隐式转换:

implicit def displayableToString[T : Displayable](target: T) =
      implicitly[Displayable[T]].getString(target)

def f[A : Displayable, B : Displayable](a: A, b: B) {
  val aIn: String = a
  val bIn: String = b
  //...
}

以下是使用示例:

f(Seq("1"), "2")
f("1", Seq("2", "3"))
f(new Date, Seq("10", "3"))
f(new Date, Seq(Seq("10", "11"), Seq("3", "4")))
f(Seq(new Date, new Date(12345)), "1")

希望这有帮助。

答案 3 :(得分:0)

关闭,非常接近...

trait StrOrSeqStr[A] {
  def mkString(x: A): String
}

implicit object Str extends StrOrSeqStr[String] { def mkString(s: String) = s }
implicit object SeqStr extends StrOrSeqStr[Seq[String]] {
    def mkString(s: Seq[String]) = s mkString " "
}

def f[A, B](a: A, b: B)(implicit evA: StrOrSeqStr[A], evB: StrOrSeqStr[B]) {
  implicit def fromSeqString(s: Seq[String]): String = s mkString ""
  val aIn: String = evA mkString a
  val bIn: String = evB mkString b
}

// both should work
f(Seq("1"), "2")
f("1", Seq("2", "3"))