Passing function to overloaded method in Scala - scala

I've hit an interesting issue with passing function references to overloaded methods in Scala (Using 2.11.7)
The following code works as expected
def myFunc(a: Int, b: String): Double = {
a.toDouble + b.toDouble
}
def anotherFunc(value: String, func: (Int, String) => Double) = {
func(111, value)
}
anotherFunc("123123", myFunc)
But the following code doesn't compile
def myFunc(a: Int, b: String): Double = {
a.toDouble + b.toDouble
}
def anotherFunc(value: String, func: (Int, String) => Double) = {
func(111, value)
}
def anotherFunc(value: Int, func: (Int, String) => Double) = {
func(value, "123123")
}
anotherFunc("123123", myFunc)
Compiler shouts the following
scala> anotherFunc("123123", myFunc)
<console>:13: error: type mismatch;
found : String("123123")
required: Int
anotherFunc("123123", myFunc)

Are you using Scala REPL? One of it's design decisions is that if you have two variables/functions with the same name defined then "last defined wins". In your case it is a function with Int parameter.
You can print all defined symbols in REPL using:
$intp.definedTerms.foreach(println)
Here someone had similar question: Why its possible to declare variable with same name in the REPL?

I don't know the reason but seems you have to write
anotherFunc("123123", myFunc _)
to make it work.

Related

Scala Curried Type Mismatch

So i got the following function after some trial and error and research in our Textbook i could come up with a solution.
def prodC1(f : Int => Int) : (Int, Int) => Int = {
def prodA1(a : Int, b : Int) : Int =
if(a > b) 1 else f(a) * prodA1(a+1, b)
prodA1 // Why do i need this line here
}
If I don't put that there i get a Type mismatch, could someone elaborate/explain the requirement for this Line?
So there are a lot of things which you need to know to actually understand the answer to this.
In Scala any def is a method which is nothing more than one of the members of some object. methods are not first class members in Scala which also means that methods can not exist on their own.
In Scala, the value of anything needs to be an expression. Meaning the RHS for a def needs to be an expression like def abc = some-expression. Examples of expression are 1, 1 + 1, "xyz", anotherMethodCallWhichWillReturnAnExpression() etc.
And something like def abc = xxxxxx is not an expression in Scala language definition. And hence you can not do,
def prodC1(f : Int => Int) : (Int, Int) => Int = {
def prodA1(a : Int, b : Int) : Int =
if(a > b) 1 else f(a) * prodA1(a+1, b)
}
Now, when you are adding that extra line with prodA1, you are telling Scala to return this prodA1 which you just define. But remember prodA1 is just a method and hence can not exist on its own and hence can not actually be returned.
But functions are first class members in Scala (represented as instances of one of the various FunctionX classes) and hence can be returned.
In this case Scala will intelligently lift this method to become a function of type (Int, Int) => Int. This is called eta-expansion.
To understand things more in detail. You can open the Scala console and try the following.
scala> val s = "abc"
// s: String = abc
scala> val i = 10
// i: Int = 10
scala> def prodA1(a : Int, b : Int) : Int = if (a > b) 1 else a * prodA1(a+1, b)
// prodA1: (a: Int, b: Int)Int
Notice the difference between the output of Scala console for actual values and def. Now, if I try to use prodA1 as value of a val, I will get following error.
scala> val x = prodA1
// <console>:12: error: missing argument list for method prodA1
// Unapplied methods are only converted to functions when a function type is expected.
// You can make this conversion explicit by writing `prodA1 _` or `prodA1(_,_)` instead of `prodA1`.
// val x = prodA1
Scala is telling you that you can explicitly convert method to function by using a _. Lets try that.
scala> val x = prodA1 _
// x: (Int, Int) => Int = $$Lambda$1077/293669143#13278a41
Now x is a function of type (Int, Int) => Int.
Also, that first line Unapplied methods are only converted to functions when a function type is expected. is telling you about what actually happened in your case.
Since prodC1 was expected to return a function of type (Int, Int) => Int and you provided prodA1, Scala used eta-expansion to automatically convert your method to function.
Let's have a look at your return type.
def prodC1(f : Int => Int) : (Int, Int) => Int = {
def prodA1(a : Int, b : Int) : Int =
if(a > b) 1 else f(a) * prodA1(a+1, b)
prodA1 // Why do i need this line here
}
your return type is
(Int, Int) => Int
that is scala sugar for a Function2[Int, Int, Int]
where the first param is the type to the first param, the second param is the type for the second param and the last is the type for the return param
The return instance needs to be a function
prodA1 conforms to this type, meaning it is allowed to be returned.
Your function needs to return an instance of (Int, Int) => Int.
def prodA1(a : Int, b : Int) : Int =
if(a > b) 1 else f(a) * prodA1(a+1, b)
Creates a function of type (Int, Int) => Int with name prodA1 but the return type of defining an inner function does not create an instance of anything so the return type of the function is Unit.
Therefore you need to return the prodA1 which has the correct type..

Use different implicit values for same type

I need to call a sort method of a library that takes an implicit Ordering parameter by using implicitly like this:
class OrderedRDDFunctions[K : Ordering : ClassTag,
V: ClassTag] (self: RDD[P]) {
private val ordering = implicitly[Ordering[K]]
def sort() = {
// uses ordering value
}
}
Now, I need to call this function twice in a loop, with different Orderings of same type like below.
var A: RDD[(Int, Int, Int)] = ...
var C: RDD[(Int, Int, Int)] = ...
while(...) {
implicit val ijOrdering:Ordering[(Int, Int, Int)] = new Ordering[(Int, Int, Int)] {
override def compare(a: (Int, Int, Int), b: (Int, Int, Int)) = {
val c = a._1.compare(b._1)
if(c != 0) c
else a._2.compare(b._2)
}
}
A.sort() // should use ijOrdering above
implicit val kjOrdering:Ordering[(Int, Int, Int)] = new Ordering[(Int, Int, Int)] {
override def compare(a: (Int, Int, Int), b: (Int, Int, Int)) = {
val c = a._3.compare(b._3)
if(c != 0) c
else a._2.compare(b._2)
}
}
C.sort() // should use kjOrdering above
}
There are two different implicit Ordering instances to be used in sort() method. But this gives me a compile error. How can I state different implicit ordering in this setup? Note that I cannot change the library method.
You can use blocks to limit the scope of implicits. A simple example:
object Blocks extends App {
def meth()(implicit e: Int) = e * 2
locally {
implicit val k = 21
println(meth()) // finds k and prints 42
}
locally {
implicit val j = 11
println(meth()) // finds j and prints 22
}
}
This does not work if there are conflicting implicits outside of the block.
Note that locally { ..stmts.. } is generally equivalent to { ..stmts.. }, tho I prefer it for readability's sake. You can read more on what's that here

High order functions in Scala

IM trying to understand the below high order functions in Scala but need some clarifications on the parameters of the functions.
Questions:-
What does the Int => String in the apply function mean?
v: Int indicates that the parameter v is of type Int.
What does the [A](x: A) mean in layout function?
object Demo {
def main(args: Array[String]) {
println( apply( layout, 10) )
}
def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString() + "]"
}
f: Int => String means that f is a function with one argument of type Int and with a return type String
def layout[A](x: A) means that parameter x is of type A, which can be of any type. Here are a couple of examples on how to invoke layout:
layout[String]("abc") //returns "[abc]"
layout[Int](123) //returns "[123]"
When main runs it invokes apply with the layout function and the argument 10. This will output "[10]"
The syntax of Int => String means passing a function that accepts Int and returns String.
Here is a useful example for passing function:
case class Person(name: String, lastName: String)
val person = Person("Johnny", "Cage")
def updateName(name: String) = {
updatePerson(_.copy(name = name))
}
def updateLastName(lastName: String) {
updatePerson(_.copy(lastName = lastName))
}
private def updatePerson(transformer: Person => Person): Unit = {
transformer(person)
}
Note how each update function passes the copy constructor.

Scala: Function with dynamic contract as argument

I have the following enum.
object Smth extends Enumeration {
class Value(name: String, func: (String, Int) => Boolean) extends super.Val(name)
private def opA(index: Int) : Boolean ={
index > 0
}
private def opB(x: String, index: Int) : Boolean ={
index > 0 && x.length > 0
}
val x = new Value("X", opA) // type mismatch error for opA
val y = new Value("Y", opB)
}
Enum constructor takes as an argument a function, of type (String, Int) => Boolean. Is it possible to create enum constructor in such a way that it will accept functions with 2 distinct contracts, for example:
(String, Int) => Boolean
(Int) => Boolean
I am trying to avoid using default value for argument, in function definition.
This is how I would like to use it.
if(Smth.x.func(0)) { do smth }
else if(Smth.y.func("str", 0)) { do smthElse }
If you just want default value you can do this:
class Value(val name: String, val func: (String, Int) => Boolean) extends super.Val(name) {
def apply(name: String, func: (Int) => Boolean): Value =
new Value(name, (x -> func("default value", x)))
}

Removing the warning message "on-variable type argument Int in type ... is unchecked" with Scala

I have a function that calculate the tuple values only when the input is a tuple of four integers.
def add(v:Any) = {
if (v.isInstanceOf[(Int, Int, Int, Int)]) {
val v2 = v.asInstanceOf[(Int, Int, Int, Int)]
println(v2._1 + v2._2 + v2._3 + v2._4)
} else {
println("NOP")
}
}
object Main extends App {
add((1,1,1,1))
add((1,2))
}
Main.main(args)
It's working, but I got a warning of "non-variable type argument ... is unchekced" warning.
warning: non-variable type argument Int in type (Int, Int, Int, Int) is
unchecked since it is eliminated by erasure
if (v.isInstanceOf[(Int, Int, Int, Int)]) {
Why this error, and what might be the best way to remove this warning message?
If you actually need to check that the argument is a tuple of four Ints, the correct way to do this is to check each component:
def add(v: Any) = v match {
case (i1: Int, i2: Int, i3: Int, i4: Int) =>
println(i1 + i2 + i3 + i4)
case _ =>
println("NOP")
}
This is caused by type erasure in compile time, you can resolve it by TypeTag:
import scala.reflect.runtime.universe._
def add[T](v:T)(implicit t: TypeTag[T]) = {
if ( t.tpe =:= typeOf[(Int, Int, Int, Int)]) {
val v2 = v.asInstanceOf[(Int, Int, Int, Int)]
println(v2._1 + v2._2 + v2._3 + v2._4)
} else {
println("NOP")
}
}
You can replace instanceOfs with pattern matching and suppress the warning with #unchecked
def add(v: Any) = v match {
case t: (Int, Int, Int, Int) #unchecked =>
println(t._1 + t._2 + t._3 + t._4)
case _ =>
println("NOP")
}
if you pass a Tuple4 that is not (Int, Int, Int, Int) you will get a ClassCastException
Error clearly says that generic types of tuple will be removed due to type erasure and hence compiler can't assure that this will work in runtime, it will only see if Tuple4 was passed, not what it contains.
The solution I presented would cause you trouble if it is possible that the function would be called with other than (Int, Int, Int, Int) Tuple4, and then you should proceed with TypeTags, otherways it just looks so much cleaner and doesn't need reflection.