Scala anonymous functions - scala

Why does my anonymous function not work?
Compiler shows me an error on line with
foreach((line: String) => {
My code:
Source.fromFile(siteDir.getPath).getLines()
.map(_.trim())
.filter(!_.isEmpty())
.foreach((line: String) => { // I got here "error: type mismatch; found : Unit required: String"
val pos = line.indexOf('=')
if (pos != -1) {
val key = line.substring(0, pos).trim()
val value = line.substring(pos + 1).trim()
readMeCache(siteId.get) + (key -> value)
}
})
What is the right way to fix it?

Related

Scala function - Type mismatch. Required: Null. Found: String

I have a problem with type mismatch in Scala Function. Compiler returns an error:
def isLarger(inputNum: Int) = {
var res = null
if (inputNum.>(10)) {
res = "aaa"
}
res
}
If inputNum larger than 10, return String "aaa" if not, return null.
But Type match.
Why? Any idea what could be the problem?
Try to add type declaration
var res: String = null
By the way, you don't need a var. If-else is an expression
def isLarger(inputNum: Int) = {
val res: String =
if (inputNum > 10)
"aaa"
else null
res
}
The concept of absent value is better expressed with Option rather than null
def isLarger(inputNum: Int) =
if (inputNum > 10)
Some("aaa")
else None
This can be shortened with Option.when (courtesy of #LuisMiguelMejíaSuárez)
def isLarger(inputNum: Int) =
Option.when(inputNum > 10)("aaa")

passing variable argument data type to a scala method

I want to create a method/function in scala which can take variable arguments of type string or int and return the result of type String or Int.
def Hello(name: String, Param: int*/string*): Int/String= {
var index = 0
while(index < Param.length) {
var n = name
var ar = Param(index)
if ( n.equals(ar) ) return Param(index + 1)
else index = index + 1
}
return Param(index.length -1)
}
If we call the Hello function then it should return the result as given below.
val Res1 = Hello("Jack", "rakesh", 1, "Jack", 2, "No one")
println(Res1)
=> this should return value 2
val Res2 = Hello("ABC", "rakesh", 1, "Jack", 2, "Vik", 3, "ram", 4, "No one")
println(Res2)
=> this should return value "No one"
Using Any should work:
def hello(name: Any, param: Any*): Any= {
var list = param.dropWhile(_ != name)
list.drop(1).headOption.orElse(param.lastOption).getOrElse("")
}
Depending on how type safe you want it to be, you can try to use generics or other means to restrict the types used. Or you can just pattern match the response type:
hello("ABC", "rakesh", 1, "Jack", 2, "Vik", 3, "ram", 4, "No one") match {
case i: Int => println("Got a int:" + i)
case s: String=> println("Got a string:" + s)
}
This will help you
def Hello( name: String,args: Any* ) = {
val index = args.indexOf(name)
if(index == -1)
args(args.length - 1)
else
args(index + 1)
}
Your whole approach is faulty, but here's how it can be done in a type-safe manner.
def Hello(name: String, param: Either[Int,String]*): Either[Int,String] = {
param.sliding(2,2)
.find(_(0).fold(_ => false, _ == name))
.fold(param.last)(_(1))
}
Usage:
Hello("Jack", Right("rakesh"), Left(1), Right("Jack"), Left(2), Right("No one"))
// res0: Either[Int,String] = Left(2)
Hello("ABC", Right("rakesh"), Left(1), Right("Jack"), Left(2),
Right("Vik"), Left(3), Right("ram"), Left(4), Right("No one"))
// res1: Either[Int,String] = Right(No one)
But it would be better to rethink it from the ground up.
I believe, what you want to achieve, is to get an index of a String element(if start counting from 1) in varargs, or return "No one". No need to pass indices to the method. You can do it like this:
def hello(name: String, params: String*): Any = {
val idx = params.indexOf(name)
if (idx != -1) idx + 1 else "No One"
}
Unfortunately both this:
def Hello(name: String, args: Any* ) = {
val index = args.indexOf(name)
if(index == -1)
args(args.length - 1)
else
args(index + 1)
}
and this:
def hello(name: String, param: Any*): Any= {
var index = 0
while(index < param.length) {
var n = name
var ar = param(index)
if ( n.equals(ar) ) return param(index + 1)
else index = index + 1
}
param(index -1)
}
are broken, as they throw an exception if you try to find the index of "No one", as index + 1 will equal to the size of the array. And it's better to compare things in Scala with == for logical equality.
But it's better not to return Any at all, but return Option[Int]:
def hello(name: String, params: String*): Option[Int] =
Option(params.indexOf(name)).filter(_ != -1).map(_ + 1)
So then you can use it like this:
val message1 = hello("Jack", "rakesh" ,"Jack").getOrElse("No one")
val message2 = hello("ABC", "rakesh", "Jack", "Vik", "ram").getOrElse("No one")
Answering the comment:
I want to know how can i pass mixed datatypes to "param".
The simplest way is to have them all of type Any
and also get string or integer as return type
The same way, defining return type as Any
The only small issue here, is that there will be no compile time check against other types. E.g. somebody might pass Boolean or any complex object along with String's and Int's to your function. But you can check at runtime against it or play with types to limit them. I don't know your requirement here, maybe it's even advantage for you.
If having Any is fine, then I would solve it like this:
def Hello(name: Any, params: Any*): Any = Option(params)
.withFilter(_.nonEmpty)
.map(_.indexOf(name))
.filter(i => i != -1 && i < params.length - 1)
.map(i => params(i + 1))
.getOrElse("No one")
Or, if you can assume, params are never empty and you have to use the last param as the default, instead of just hard coded "No one":
def Hello(name: Any, params: Any*): Any = Option(params)
.withFilter(_.nonEmpty)
.map(_.indexOf(name))
.filter(i => i != -1 && i < params.length - 1)
.map(i => params(i + 1))
.getOrElse(params.last)
Notice the check against "No one" attack: i < params.length - 1.
Notice that name now is also of type Any.
Now, even if you pass "No one" as a name, the Option will evaluate to None thanking to the filter, and getOrElse will give you the default "No one" instead of an exception.

dynamic string interpolation

I would like to pretty-print a Product, such as a case class, so I create the following trait:
trait X extends Product {
def fmtStrs =
productIterator map {
case _ : Double => "%8.2f"
case _ => "%4s"
} map (_ + separator) toSeq
override def toString = {
new StringContext("" +: fmtStrs : _*) f (productIterator.toSeq : _*)
}
}
This uses string interpolation as described in the ScalaDoc for StringContext.
But this won't compile, with this cryptic error:
Error:(69, 70) too many arguments for interpolated string
new StringContext("" +: fmtStrs : _*) f (productIterator.toSeq : _*)
Is this a bug, or limitation of a macro? Note that doing the following works fine, so I suspect this may be related to the variable argument list:
scala> val str2 = StringContext("","%4s,","%8.2f").f(1,23.4)
str2: String = " 1, 23.40"
The reason f is a macro is so that it can give you an error when types of format specifiers and arguments don't match, and this isn't possible to check by looking at ("" +: fmtStrs : _*) and (productIterator.toSeq : _*), so it isn't particularly surprising this doesn't work. The error message could be clearer, so let's see what exactly happens.
If you look at the implementation of f (it took me some time to actually find it, I finally did by searching for the error message), you'll see
c.macroApplication match {
//case q"$_(..$parts).f(..$args)" =>
case Applied(Select(Apply(_, parts), _), _, argss) =>
val args = argss.flatten
def badlyInvoked = (parts.length != args.length + 1) && truly {
def because(s: String) = s"too $s arguments for interpolated string"
val (p, msg) =
if (parts.length == 0) (c.prefix.tree.pos, "there are no parts")
else if (args.length + 1 < parts.length)
(if (args.isEmpty) c.enclosingPosition else args.last.pos, because("few"))
else (args(parts.length-1).pos, because("many"))
c.abort(p, msg)
}
if (badlyInvoked) c.macroApplication else interpolated(parts, args)
With your call you have a single tree in both parts and argss, and parts.length != args.length + 1 is true, so badlyInvoked is true.
s doesn't care what its arguments look like, so it's just a method and your scenario works.

scala variable number by name parameters [duplicate]

This question already has answers here:
Scala variable argument list with call-by-name possible?
(2 answers)
Closed 6 years ago.
I am trying to implement a control flow structure which can accept a variable number of by-name parameters.
See CalculateGroup method and its use.
I was trying to follow this post, but still have some issues
As I can see from the error, I suspect I need to define a type annotation predicate in CalculateGroup function?
Here is current code:
def compare[T : Numeric](x: T)(y: T) : Boolean = implicitly[Numeric[T]].gt( x, y )
val items = compare[Double](10) _
val assertionsEnabled = true
def Calculate( predicate: => Boolean ) =
if (assertionsEnabled && !predicate)
throw new AssertionError
Calculate{
items(5)
}
def CalculateGroup( list: (predicate: => Boolean) *) =
{
list.foreach( (p : (predicate: => Boolean) ) => {
if (assertionsEnabled && !predicate)
throw new AssertionError
})
}
CalculateGroup{
items(5),
items(3),
items(8)
}
Error details:
scala ControlFlow.scala
/Users/pavel/Documents/ControlFlow/ControlFlow.scala:36: error: ')' expected but ':' found.
def CalculateGroup( list: (predicate: => Boolean) *) =
^
/Users/pavel/Documents/ControlFlow/ControlFlow.scala:68: error: ')' expected but '}' found.
}
^
two errors found
You cannot use by-name var args, you could use a lazy collection like Iterator or maybe Stream:
def compare[T : Numeric](x: T)(y: T) : Boolean = implicitly[Numeric[T]].gt( x, y )
val items = compare[Double](10) _
val assertionsEnabled = true
def Calculate(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
Calculate{
items(5)
}
def CalculateGroup(list: Iterator[Boolean]) =
{
list.foreach { (p : Boolean ) =>
if (assertionsEnabled && !p) {
throw new AssertionError
}
}
}
CalculateGroup{Iterator(
items(5),
items(3),
items(8)
)}
You have a syntax problem... you are placing a colon in front of the word predicate in the signature of the method CalculateGroup and in the foreach. Just remove them and it should compile.
just remove it and know that the word predicate is not alias for a variable, but it should be the name of a class. So it's better if you capitalize it. Contrary to the case of your methods, which shouldn't be capitalized.
Update
To have multiple by-name parameters just do this:
def CalculateGroup( list: (=> Boolean) *) =
{
list.foreach( (p : (=> Boolean) ) => {
if (assertionsEnabled && !p)
throw new AssertionError
})
}

Scala underspecified types

I have the following spark code snippet .
But get the following error:
:8: error: missing parameter type.
which happens here:
val index:Int= col1(i) ; tokened +=splitted(index) + " " ; } }
^
I can't work out where it steps from as it seems like i've specified all properties. I also need to return the string so for the method to be string => string (currently it's string -> unit) This is my first time ever coding in scala so apologies if this is a stupid question
line => { var col1:Array[Int] = Array(1,2) ; var tokened:String = "" ;
var splitted:Array[String]=line.split(" ") ;
for (i<- 0 to col1.length) {
val index:Int= col1(i);
tokened +=splitted(index) + " " ;
}
}
I guess this is what you need:
(line: String) => { /* Previously missing type annotation */
var col1:Array[Int] = Array(1,2)
var tokened:String = ""
var splitted:Array[String]=line.split(" ")
for (i<- 0 to col1.length) {
val index:Int= col1(i)
tokened += splitted(index) + " "
}
tokened /* Return value */
}
In order to make the function type explicit, you could store the anonymous function in a function-typed variable:
val f: (String => String) = line => { ... }