Passing a individual arguments AND a Seq to a var-arg function - scala

I know it's possible to pass individual arguments to a vararg function and it's possible to pass a seq using :_* but is it possible to pass both?
for example:
scala> object X { def y(s: String*) = println(s) }
defined module X
scala> X.y("a", "b", "c")
WrappedArray(a, b, c)
scala> X.y(Seq("a", "b", "c"):_*)
List(a, b, c)
scala> X.y("a", Seq("b", "c"):_*)
<console>:9: error: no `: _*' annotation allowed here
(such annotations are only allowed in arguments to *-parameters)
X.y("a", Seq("b", "c"):_*)
^
Edit: In Scala 2.10 (in case that matters)

Hacky but this should work well:
X.y(Seq("a") ++ Seq("b", "c"):_*)

If you look around in the Scala standard library you'll find this sort of pattern in places:
def doIt(arg: Thing)
def doIt(arg1: Thing, arg2: Thing, moreArgs: Thing*)
You can see this, e.g., in Set.+(...). It allows you to have any number of arguments without ambiguity in the overloads.
Addendum
Proof of concept:
scala> class DI { def doIt(i: Int) = 1; def doIt(i1: Int, i2: Int, iMore: Int*) = 2 + iMore.length }
defined class DI
scala> val di1 = new DI
di1: DI = DI#16ac0be1
scala> di1.doIt(0)
res1: Int = 1
scala> di1.doIt(1, 2)
res2: Int = 2
scala> di1.doIt(1, 2, 3)
res3: Int = 3
scala> di1.doIt(1, 2, List(3, 4, 5): _*)
res4: Int = 5

Related

Represent T* as concrete type

(T,T)* resolves to Seq[(T,T)] after erasure, but how to represent (T,T)* itself as a type?
The reason I ask is there's an API I'm using that defines a:
def foo(bar: (String,String)*) = ...
but fails when I try to pass in a Seq[(String,String)].
My pull request to add in:
def foo(bar: Seq[(String,String)]) = ...
blows up due to the 2 methods having the same type after erasure.
Are star projections able to be represented as a concrete type?
You can pass the Seq if you follow it with :_* like this:
val s:Seq[(String, String)] = Seq( ("a", "b"), ("c", "d"), ... )
foo(s:_*)
So you shouldn't need both signatures.
To disambiguate the erased signatures:
scala> class X { def f(is: Int*) = is.sum }
defined class X
scala> class Y extends X { def f(is: Seq[Int])(implicit d: DummyImplicit): Int = f(is: _*) }
defined class Y
scala> new Y().f(1 to 10)
res3: Int = 55
or this is better, the signatures in collections always look like this to mean "two or more":
scala> class X {
| def f(i: Int): Int = i
| def f(is: Seq[Int]): Int = is.sum
| def f(i: Int, j: Int, rest: Int *): Int = i + j + rest.sum
| }
defined class X
scala> new X().f(3)
res9: Int = 3
scala> new X().f(3,4)
res10: Int = 7
scala> new X().f(3,4,5)
res11: Int = 12
scala> new X().f(1 to 10)
res12: Int = 55
You can't refer to a repeated parameter type, just as you can't refer to a by-name parameter type as such. So you can't convert to it. However, you can detect it reflectively, by name:
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> typeOf[X].member(TermName("f")).asMethod.paramss.flatten.head.asTerm.typeSignature.typeSymbol.name
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
res4: reflect.runtime.universe.Symbol#NameType = <repeated>
There is internal API, definitions.isRepeated(sym), if you want to cast for it.

Use 4 (or N) collections to yield only one value at a time (1xN) (i.e. zipped for tuple4+)

scala> val a = List(1,2)
a: List[Int] = List(1, 2)
scala> val b = List(3,4)
b: List[Int] = List(3, 4)
scala> val c = List(5,6)
c: List[Int] = List(5, 6)
scala> val d = List(7,8)
d: List[Int] = List(7, 8)
scala> (a,b,c).zipped.toList
res6: List[(Int, Int, Int)] = List((1,3,5), (2,4,6))
Now:
scala> (a,b,c,d).zipped.toList
<console>:12: error: value zipped is not a member of (List[Int], List[Int], List[Int], List[Int])
(a,b,c,d).zipped.toList
^
I've searched for this elsewhere, including this one and this one, but no conclusive answer.
I want to do the following or similar:
for((itemA,itemB,itemC,itemD) <- (something)) yield itemA + itemB + itemC + itemD
Any suggestions?
Short answer:
for (List(w,x,y,z) <- List(a,b,c,d).transpose) yield (w,x,y,z)
// List[(Int, Int, Int, Int)] = List((1,3,5,7), (2,4,6,8))
Why you want them as tuples, I'm not sure, but a slightly more interesting case would be when your lists are of different types, and for example, you want to combine them into a list of objects:
case class Person(name: String, age: Int, height: Double, weight: Double)
val names = List("Alf", "Betty")
val ages = List(22, 33)
val heights = List(111.1, 122.2)
val weights = List(70.1, 80.2)
val persons: List[Person] = ???
Solution 1: using transpose, as above:
for { List(name: String, age: Int, height: Double, weight: Double) <-
List(names, ages, heights, weights).transpose
} yield Person(name, age, height, weight)
Here, we need the type annotations in the List extractor, because transpose gives a List[List[Any]].
Solution 2: using iterators:
val namesIt = names.iterator
val agesIt = ages.iterator
val heightsIt = heights.iterator
val weightsIt = weights.iterator
for { name <- names }
yield Person(namesIt.next, agesIt.next, heightsIt.next, weightsIt.next)
Some people would avoid iterators because they involve mutable state and so are not "functional". But they're easy to understand if you come from the Java world and might be suitable if what you actually have are already iterators (input streams etc).
Shameless plug-- product-collections does something similar:
a flatZip b flatZip c flatZip d
res0: org.catch22.collections.immutable.CollSeq4[Int,Int,Int,Int] =
CollSeq((1,3,5,7),
(2,4,6,8))
scala> res0(0) //first row
res1: Product4[Int,Int,Int,Int] = (1,3,5,7)
scala> res0._1 //first column
res2: Seq[Int] = List(1, 2)
val g = List(a,b,c,d)
val result = ( g.map(x=>x(0)), g.map(x=>x(1) ) )
result : (List(1, 3, 5, 7),List(2, 4, 6, 8))
basic, zipped assit tuple2 , tuple3
http://www.scala-lang.org/api/current/index.html#scala.runtime.Tuple3Zipped
so, You want 'tuple4zippped' you make it
gool luck
found a possible solution, although it's very imperative to my taste:
val a = List(1,2)
val b = List(3,4)
val c = List(5,6)
val d = List(7,8)
val g : List[Tuple4[Int,Int,Int,Int]] = {
a.zipWithIndex.map { case (value,index) => (value, b(index), c(index), d(index))}
}
zipWithIndex would allow me to go through all the other collections. However, i'm sure there's a better way to do this. Any suggestions?
Previous attempts included:
Ryan LeCompte's zipMany or transpose.
however, it a List, not a tuple4. this is not as convenient to work with since i can't name the variables.
Tranpose it's already built in in the standard library and doesn't require higher kinds imports so it's preferrable, but not ideal.
I also, incorrectly, tried the following example with Shapeless
scala> import Traversables._
import Tuples._
import Traversables._
import Tuples._
import scala.language.postfixOps
scala> val a = List(1,2)
a: List[Int] = List(1, 2)
scala> val b = List(3,4)
b: List[Int] = List(3, 4)
scala> val c = List(5,6)
c: List[Int] = List(5, 6)
scala> val d = List(7,8)
d: List[Int] = List(7, 8)
scala> val x = List(a,b,c,d).toHList[Int :: Int :: Int :: Int :: HNil] map tupled
x: Option[(Int, Int, Int, Int)] = None

What is the type of a variable-length argument list in Scala?

Suppose that I declare a function as follows:
def test(args: String*) = args mkString
What is the type of args?
This is called a variable number of arguments or in short varargs. It's static type is Seq[T] where T represents T*. Because Seq[T] is an interface it can't be used as an implementation, which is in this case scala.collection.mutable.WrappedArray[T]. To find out such things it can be useful to use the REPL:
// static type
scala> def test(args: String*) = args
test: (args: String*)Seq[String]
// runtime type
scala> def test(args: String*) = args.getClass.getName
test: (args: String*)String
scala> test("")
res2: String = scala.collection.mutable.WrappedArray$ofRef
Varargs are often used in combination with the _* symbol, which is a hint to the compiler to pass the elements of a Seq[T] to a function instead of the sequence itself:
scala> def test[T](seq: T*) = seq
test: [T](seq: T*)Seq[T]
// result contains the sequence
scala> test(Seq(1,2,3))
res3: Seq[Seq[Int]] = WrappedArray(List(1, 2, 3))
// result contains elements of the sequence
scala> test(Seq(1,2,3): _*)
res4: Seq[Int] = List(1, 2, 3)

Can scala splat be used for anything that isn't a varargs?

given e.g:
scala> def pipes(strings:String*) = strings.toList.mkString("|")
which I can call normally:
scala> pipes("foo", "bar")
res1: String = foo|bar
or with a splat:
scala> val args = List("a","b","c")
scala> pipes(args:_*)
res2: String = a|b|c
But can I use a splat to provide arguments for anything but a varargs parameter? e.g I would like to do something like:
scala> def pipeItAfterIncrementing(i:Int, s:String) = (i + 1) + "|" + s
scala> val args:Tuple2[Int, String] = (1, "two")
scala> pipeItAfterIncrementing(args:_*)
That doesn't work, but is there any way to achieve the same effect of providing multiple arguments from a single object, whether it be a tuple or something else? Is there any reason this couldn't be implemented for tuples, given that both their length and types are known at compile-time?
You can use Function.tupled to do exactly this: turn a function that takes n arguments into a function that takes a single tuple argument of arity n. As can be expected, Function.untupled does the reverse job.
The special type ascription : _* is only applicable for repeated parameter (a.k.a. varargs).
scala> def pipeItAfterIncrementing(i:Int, s:String) = (i + 1) + "|" + s
pipeItAfterIncrementing: (i: Int,s: String)java.lang.String
scala> def tupledPipeItAfterIncrementing = Function.tupled(pipeItAfterIncrementing _)
tupledPipeItAfterIncrementing: ((Int, String)) => java.lang.String
scala> val args:Tuple2[Int, String] = (1, "two")
args: (Int, String) = (1,two)
scala> tupledPipeItAfterIncrementing(args)
res0: java.lang.String = 2|two
Well kind of ...
scala> def pipeItAfterIncrementing(i:Int, s:String) = (i + 1) + "|" + s
scala> val args:Tuple2[Int, String] = (1, "two")
scala> (pipeItAfterIncrementing _).tupled(args)
will give you the desired 2|two.

In scala can I pass repeated parameters to other methods?

Here is something I can do in java, take the results of a repeated parameter and pass it to another method:
public void foo(String ... args){bar(args);}
public void bar(String ... args){System.out.println("count="+args.length);}
In scala it would look like this:
def foo(args:String*) = bar(args)
def bar(args:String*) = println("count="+args.length)
But this won't compile, the bar signature expects a series of individual strings, and the args passed in is some non-string structure.
For now I'm just passing around arrays. It would be very nice to use starred parameters. Is there some way to do it?
Java makes an assumption that you want to automatically convert the Array args to varargs, but this can be problematic with methods that accept varargs of type Object.
Scala requires that you be explicit, by ascribing the argument with : _*.
scala> def bar(args:String*) = println("count="+args.length)
bar: (args: String*)Unit
scala> def foo(args:String*) = bar(args: _*)
foo: (args: String*)Unit
scala> foo("1", "2")
count=2
You can use : _* on any subtype of Seq, or on anything implicitly convertable to a Seq, notably Array.
scala> def foo(is: Int*) = is.length
foo: (is: Int*)Int
scala> foo(1, 2, 3)
res0: Int = 3
scala> foo(Seq(1, 2, 3): _*)
res1: Int = 3
scala> foo(List(1, 2, 3): _*)
res2: Int = 3
scala> foo(Array(1, 2, 3): _*)
res3: Int = 3
scala> import collection.JavaConversions._
import collection.JavaConversions._
scala> foo(java.util.Arrays.asList(1, 2, 3): _*)
res6: Int = 3
It is explained well with examples in the Language Reference, in section 4.6.2.
Note that these examples are made with Scala 2.8.0.RC2.