7

I have library method taking variable argument list and producing data

class Data
def process(elems: String*): Data = new Data

and I want my strings to be implicitly converted to Data

implicit def strToData(ts: String): Data = process(t)

so I can write something like

val data: Data = "one"

but I want tuples of strings to be implicitly converted too. I've added another implicit

implicit def strsToData(ts: String*): Data = process(ts: _*)

it compiles fine, but conversion fails

val data: Data = ("one", "two")
val dat3: Data = ("one", "two", "three")
val dat4: Data = ("one", "two", "three", "four")

with

found   : Seq[java.lang.String]
required: this.Data
val data: Data = Seq("one", "two")

Is there any way to convert tuples implicitly, or a reason why it can be achieved?

Update: Tuples can be of any arity.

1

3 Answers 3

5
  1. ts: String* is not a tuple, but an Iterable. You can't convert iterable to tuple, because tuple is a static type and its arity is resolved at compile time. Emil H answered how you can do an implicit conversion from tuple.
  2. Transparent implicit conversions from one type to another type is a common pitfall and are greatly discouraged. Instead you should apply a "wrapper" approach, or use "Value Classes" since Scala 2.10:

    In Scala 2.9:

    implicit def stringStringTupleExtender (ts : (String, String)) =
      new {
        def data = process(ts)
      }
    

    In Scala 2.10:

    implicit class StringStringTupleExtender (val ts : (String, String)) extends AnyVal {
      def data = process(ts)
    }
    

    Then you'll use it like so:

    val data : Data = ("sdf", "lsdfj").data
    val data1 : Data = "sdf".data // if you do the same for String
    
  3. If you're looking for dynamically resolving any input collection, then, guess what, you should use a collection, not a tuple.

    In Scala 2.9

    implicit def seqExtender (ts : Seq[String]) =
      new {
        def data = process(ts)
      }
    

    Usage:

    val data : Data = Seq("sdf", "lsdkfjsdl", "ldsfjk").data
    
Sign up to request clarification or add additional context in comments.

Comments

3

While all the warnings in @NikitaVolkov's answer apply in double force, you can write a version that accepts any arity of tuple, as well as any case class:

implicit def prod2data(Product p): process((p.productIterator.toSeq):_*)

Comments

2

You would need to create a implicit conversion from a tuple to Data:

implicit def strTplToData(ts:(String, String)) = process(ts._1, ts._2)

Then you could do:

val data: Data = ("one", "two")

2 Comments

Is there another solution than to write function for every tuple arity?
I do not think there is an elegant way of achieving that simply.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.