suppose i have a method that takes a large number of arguments, each of which could be a String or a Seq[String]. inside the method, i want the value of each argument to be just the value if it was a String, and mkString " " on the Seq if it was a Seq[String]. i realize that Left/Right could possibly be used here but i don't want the caller to have to wrap any args.
i've used implicits to force the arg to be either a String or a Seq[String] but i can't figure out how to get the arg to reduce itself to a string, either as the identity or as mkString, depending on the case. the ideal code might look like this:
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开发者_StackOverflow should work
f(Seq("1"), "2")
f("1", Seq("2", "3"))
any suggestions? thanks!
Please correct me if I'm wrong, but as far as I understand you just need to convert String
to Seq[String]
implicitly. This simple implicit conversion should do it:
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"))
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
As far as I can see you tried to use context bounds in order to give function f
something that can be treated as String
or can be converted to it. I addition to my other answer I want to add other solution that uses type classes. It's more complicated, so I can advise you to use simple implicit conversion, that I described in another answer, if it solves your problem.
For demonstration purposes I renamed StrOrSeqStr
to Displayable
and made it more generic, so that it works not only for String
and Seq[String]
, but also for any other types and Seq[T]
if type parameter T
is also has Displayable
for it (it actually can work with any class you want - you just need to provide suitable implicit).
At first you need to define 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 " "
}
}
As you can see I collected all related implicits in companion object, so that they would be always available and do not require explicit import (good for future usage).
Interesting method here is seqDisplayable
- it requires T
to also have Displayable
(this also makes it recursive - so that Seq[Seq[Date]]
for example would also have Displayable
). I also created special rules for String
and Date
. So as you can see, it's pretty restrictive and allows String
, Date
, Seq[String]
, Seq[Date]
, Seq[Seq[String]]
, Seq[Seq[String]]
, etc. If you want to allow all other objects you can add something like this:
implicit def anythingDisplayable[T] = new Displayable[T] {
def getString(anything: T) = anything toString
}
Now comes f
function:
def f[A : Displayable, B : Displayable](a: A, b: B) {
val aIn = implicitly[Displayable[A]].getString(a)
val bIn = implicitly[Displayable[B]].getString(b)
// ...
}
So I require evidence: there should exist some Displayable
for A
and B
and then I just convert them to String
using these Displayable
s. If you want to simplify it further, you can create implicit conversion from some type that has Dislplayable
to String
:
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
//...
}
Here is example of usage:
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")
Hope this helps.
Close, very close...
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"))
精彩评论