Scala class constructor with variable number of arguments - scala

If we pass in a list to a method that takes a variable number of arguments it works.
val testList = List("a", "b", "c")
def testMethod(str: String*): Seq[String] = str
testMethod(testList) // outputs WrappedArray(List("a", "b", "c"))
But if we pass in a list to a class constructor that takes a variable number of arguments, we get a type error.
val testList = List("a", "b", "c")
class TestClass(str: String*)
val t = new TestClass(testList)
// error: type mismatch
// found: List[String]
// required: [String]
Any idea how we can fix this?

It's not working in neither case (note the unwanted WrappedArray in the first case). In order to pass a sequence as a variable-argument list, you need to treat it as such. The syntax for it is the same. In the first case:
testMethod(testList: _*)
and in the second case:
val t = new testClass(testList: _*)
You can interpret this notation in a similar fashion of variable-arguments syntax, the only difference being that here the type is not explicitly stated (underscore is used instead).

Related

Automatic type recognition in Scala

I am learning Scala right now. I see that specifying type while assigning to new val is not necessary. But then consider the following code:
object MyObject {
def firstResponse(r: Array[String]): String = r(0)
def mostFrequent(r: Array[String]): String = {
(r groupBy identity mapValues (_.length) maxBy(_._2))._1
}
def mostFrequent(r: Array[String], among: Int): String = { mostFrequent(r take among) }
// throws compile error
val heuristics = Array(
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
}
If I change the last line and specify the type explicitly, then the error is gone
val heuristics: Array[Array[String] => String] = Array(
firstResponse, mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
What's wrong here?
Edit: As #mdm correctly pointed out,
//This works
val heuristics = Array(firstResponse(_), firstResponse(_))
//This does not work
val heuristics = Array(mostFrequent(_,1), mostFrequent(_,2))
Open question is, why Scala can determine the type of firstResponse(_) correctly while it has difficulty to do the same for mostFrequent(_,1).
The compiler complains with something similar to this:
Error:(28, 29) missing parameter type for expanded function ((x$3: ) => mostFrequent(x$3, 3))
As you probably already figured out, that happens because the compiler cannot figure out automatically (infer) the type of the input parameter of those functions, when you use _. More precisely, it can't infer the type of mostFrequent(_, 3).
So, if you give the compiler a nudge, either by val heuristics: Array[Array[String] => String] = or by the following:
val heuristics = Array(
(a : Array[String]) => firstResponse(a),
(a : Array[String]) => mostFrequent(a, 3),
(a : Array[String]) => mostFrequent(a, 4),
(a : Array[String]) => mostFrequent(a, 5)
)
Things will work as expected.
Looking at posts about _ uses like this or this, you will see that it can mean very many things, depending on the context. In this case I suspect the confusion comes from the fact that you are using _ to transform a call to a method with more than one parameter to an anonymous function.
Notice that both of the following will work fine:
val heuristics = Array(
firstResponse(_),
firstResponse(_),
firstResponse(_)
)
val heuristics2 = Array(
firstResponse(_),
mostFrequent(_: Array[String], 3)
)
As to the specific reason why a method with more than one argument cannot be transformed into an anonymous function, while one with one argument can, I will delegate to someone with more in-depth knowledge of the compiler's inference mechanics.
Sometimes when you use underscores as placeholders for parameters,
the compiler might not have enough information to infer missing parameter
types. Therefore, you need to explicitly provide type information. Placeholder syntax act as a “blank” in the expression that needs to be “filled in" and you can fill any value to it. Therefore, compiler will have no information about the type of this placeholder.
val foo = _ + _
//will fail - error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
The above expression will fail, because compiler will unable to find type of value that fill the placeholder. Therefore, there need to be some way for compiler to know the type. The one way is to provide type information of variable/method explicitly.
val foo: (String, String) => String = _ + _
The above expression will successfully compiled. Because, compiler resolve type of the parameter from type of variable foo (1st and 2nd placeholder are both as String).
In certain case, compiler can resolve the type from value:
List(1,2,3).foreach(println(_))
In above case, List(1,2,3) is a List of type Int, hence compiler will know type information of placeholder in println(_) as Int which is resolved from value of List.
In addition, you can also provide type of value explicitly in order to let compiler know about type.
val foo = (_:String) + (_:String) //will return function (String, String) => String
In certain case, if your method have only one parameter, then you don't need to provide explicit type parameter otherwise you need to provide type for placeholder syntax as below:
scala> def firstResponse(r: Array[String]): String = r(0)
firstResponse: (r: Array[String])String
scala> val foo = firstResponse(_) //no need to provide type information
foo: Array[String] => String = <function1>
scala> def firstResponse2(r: Array[String], index:Int): String = r(index)
firstResponse2: (r: Array[String], index: Int)String
scala> val foo = firstResponse2(_, 3) //will fail, need to provide type information.
<console>:12: error: missing parameter type for expanded function ((x$1) => firstResponse2(x$1, 3))
val foo = firstResponse2(_, 3)
^
scala> val foo = firstResponse2((_:Array[String]), 3)
foo: Array[String] => String = <function1>
Now coming to your case:
val heuristics = Array(
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
Here, compiler will have no idea of what is the type because:
val heuristics have no type
Type for placeholder syntax is not explicitly provided.
You have solve the issue by providing type Array[Array[String] => String] to heuristics val as in case 1, and hence compiler compiles it fine.
For case 2, you can modify your code as below:
val heuristics = Array(
firstResponse(_), mostFrequent(_:Array[String], 3), mostFrequent(_:Array[String], 4), mostFrequent(_:Array[String], 5)
)
The weird thing is that val foo = firstResponse(_) works, because the specification directly forbids it:
If there is no expected type for the function literal, all formal parameter types Ti must be specified explicitly, and the expected type of e is undefined.
I thought that it could be treated as equivalent to eta-expansion firstResponse _ which worked without expected type because firstResponse isn't overloaded, but it's defined to be the other way around: firstResponse _ means the same as x => firstResponse(x), which is not supposed to work according to the above quote.
So strictly speaking, it appears to be a bug and you should write firstResponse(_: Array[String]) as well.
Though in this case, to avoid repetition I'd provide the expected type as
val heuristics = Array[Array[String] => String](
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)

Default value for variable arguments in scala method

I want to define a default value for a variable argument parameter in a Scala method. Is it possible?
def aMethod(variableArguments: String* = ???){}
I have tried using variableArguments:String* = Seq("a","b","c"):_* but it doesn't work.
The compiler error makes it pretty clear-cut:
:10: error: a parameter section with a `*'-parameter is not allowed to have default arguments
You can work around this with overloading, if you wish:
def test(a: String*): String = a.mkString
def test(): String = test("a", "b", "c")

Scala mkString equivalent in Scoobi Dlist

I am trying to use folds or better yet, mkString to concatenate the strings I have in a DList
val dlist = DList("a", "b", "c")
so that I get a single string : "abc"
Apparently, for a regular list in scala, list.mkString does the job.
Merging a list of Strings using mkString vs foldRight
is there an easy way to do it in scoobi's Distributed Lists?
There's a method DList.fold:
/**Sum up the elements of this distributed list. */
def fold(implicit m: Monoid[A]): DObject[A] =
reduceOption(R.semigroup) map (_ getOrElse m.zero)
Since the type A in your case is String, and scalaz provides an instance Monoid[String], we can use DList.fold:
// make sure there's a Monoid[String] instance in scope
import scalaz._, Scalaz._
val xs = DList("a","b","c")
xs.fold

Call a function with arguments from a list

Is there a way to call a function with arguments from a list? The equivalent in Python is sum(*args).
// Scala
def sum(x: Int, y: Int) = x + y
val args = List(1, 4)
sum.???(args) // equivalent to sum(1, 4)
sum(args: _*) wouldn't work here.
Don't offer change the declaration of the function anyhow. I'm acquainted with a function with repeated parameters def sum(args: Int*).
Well, you can write
sum(args(0), args(1))
But I assume you want this to work for any list length? Then you would go for fold or reduce:
args.reduce(sum) // args must be non empty!
(0 /: args)(sum) // aka args.foldLeft(0)(sum)
These methods assume a pair-wise reduction of the list. For example, foldLeft[B](init: B)(fun: (B, A) => B): B reduces a list of elements of type A to a single element of type B. In this example, A = B = Int. It starts with the initial value init. Since you want to sum, the sum of an empty list would be zero. It then calls the function with the current accumulator (the running sum) and each successive element of the list.
So it's like
var result = 0
result = sum(result, 1)
result = sum(result, 4)
...
The reduce method assumes that the list is non-empty and requires that the element type doesn't change (the function must map from two Ints to an Int).
I wouldn't recommend it for most uses since it's a bit complicated and hard to read, bypasses compile-time checks, etc., but if you know what you're doing and need to do this, you can use reflection. This should work with any arbitary parameter types. For example, here's how you might call a constructor with arguments from a list:
import scala.reflect.runtime.universe
class MyClass(
val field1: String,
val field2: Int,
val field3: Double)
// Get our runtime mirror
val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)
// Get the MyClass class symbol
val classSymbol = universe.typeOf[MyClass].typeSymbol.asClass
// Get a class mirror for the MyClass class
val myClassMirror = runtimeMirror.reflectClass(classSymbol)
// Get a MyClass constructor representation
val myClassCtor = universe.typeOf[MyClass].decl(universe.termNames.CONSTRUCTOR).asMethod
// Get an invokable version of the constructor
val myClassInvokableCtor = myClassMirror.reflectConstructor(myClassCtor)
val myArgs: List[Any] = List("one", 2, 3.0)
val myInstance = myClassInvokableCtor(myArgs: _*).asInstanceOf[MyClass]

Option[Map[String, String]] can get a value weirdly

I found Option[Map[String, String]] works weirdly like this:
scala> val fileInfo: Option[Map[String, String]] = Some(Map( "type" -> "hoge" ))
fileInfo: Option[Map[String,String]] = Some(Map(type -> hoge))
scala> fileInfo.get("type")
res1: String = hoge
I think the Option "get" method doesn't take any argument, so this is super weird for me. Why does it work? Is it an implicit conversion or a bug? I want to make sure how it works.
My specifications are:
Scala version 2.9.2
Java 1.6.0_43
This desugars to fileInfo.get.apply("type"), i.e. you are not really passing a parameter to Option#get, but calling .apply("type") on the result.
Scala allows you to omit braces in some cases and you've been a victim of ambiguity that this feature created: what you've done is simply unwrapped Option (and get underlying Map) -- Option has not only widely used .getOrElse, but also unsafe .get method which fails with exception when there is None:
val fileInfo: Option[Map[String, String]] = Some(Map( "type" -> "hoge" ))
val map = fileInfo.get
// map: Map[String,String] = Map(type -> hoge)
// now actual map lookup
map("type")
// res0: String = hoge
// but this obviously won't work
val throwy: Option[Map[String, String]] = Option(null) // None
throwy.get("type")
// java.util.NoSuchElementException: None.get
fileInfo.get("type")
is translated to:
fileInfo.get().apply("type")
So you unwrap the option and then get an element on the Map. (apply gets an element of the map non-optional, i.e. fails if the key doesn't exist).