In a comment for SIP-13 Martin Odersky implied that it is possible to create an implicit method with multiple arguments. According to my experiences, implicit methods always have exactly one argument and I cannot imagine how an implicit method with multiple arguments can be used. Can someone give some use case and explanation?
For example if you need an implicit parameter of a function type:
implicit def foo(x: Int, y: Int) = y * x
def bar(x: Int, y: Int)(implicit f: (Int, Int) => Int) = f(x,y)
scala> bar(3,4)
res3: Int = 12
Related
What is the scala compiler doing here?
def f[A](x: A): A = {
x
}
f(1,2,3)
res0: (Int, Int, Int) = (1,2,3)
I'm not asking x to be a variadic argument. However the compiler seems to be casting the argument list to a product type.
If I specify the type, eg
def f(x: Int): Int = {
x
}
and then try to call
f(1,2,3)
The compiler argues that too many arguments (3) for method f
Shouldn't then it say Expected Int and found (Int, Int, Int)?
Why is this?
The compiler infers the type A to be Tuple3[Int, Int, Int] a.k.a. (Int, Int, Int). It provides syntactic facilities that turn f(1,2,3) into f((1,2,3)).
Started learning Scala today, and I was curious if you can overload a function to add currying like:
def add(x: Int, y: Int): Int = x + y
def add(x: Int)(y: Int): Int = x + y
But not only does this code not compile, but I've heard that overloading in Scala is not a good idea.
Is there a way to overload add such that it's curried without doing partial application, meaning both add(1, 2) and add(1)(2) work?
The problem is that those add functions are indistinguishable after JVM type erasure: during execution they are both (Int, Int)Int. But they are different during compilation, and Scala compiler can tell which one you are calling.
This means you have to make their argument lists different. To achieve that you can add an implicit argument list with a DummyImplicit argument:
def add(x: Int, y: Int)(implicit dummy: DummyImplicit): Int = x + y
def add(x: Int)(y: Int): Int = x + y
This DummyImplicit is provided by Scala library, and there is always an implicit value for it. Now the first function after erasure has a type (Int, Int, DummyImplicit)Int, and the second one (Int, Int)Int, so JVM can distinguish them.
Now you can call both:
add(1, 2)
add(1)(2)
I've got a way you can use add(1,2) and add(1)(2) but I wouldn't recommend it. It uses Scala's implicit definitions to have a different type for both methods, but use an implicit method to convert to the appropriate type.
case class IntWrapper(value: Int) // Purely to have a different type
object CurryingThing
{
def add(x: IntWrapper)(y: IntWrapper) = x.value + y.value
def add(x: Int, y: Int) = x + y
// The magic happens here. In the event that you have an int, but the function requires an intWrapper (our first function definition), Scala will invoke this implicit method to convert it
implicit def toWrapper(x: Int) = IntWrapper(x)
def main(args: Array[String]) = {
// Now you can use both add(1,2) and add(1)(2) and the appropriate function is called
println(add(1,2)) //Compiles, prints 3
println(add(1)(2)) // Compiles, prints 3
()
}
}
For overloading it's necessary that function should either:
have different number of arguments
or have different argument type(s)
In your example both definitions of add are equivalent hence it's not overloading and you get compilation error.
You could use Kolmar's way (implicit object) below to call both add(1, 2) and add(1)(2) OR you can use Scala's Default Parameter to achieve same thing:
def add(a: Int, b: Int, c: Any = DummyImplicit) = a + b // c is default parameter
def add(a: Int)(b: Int) = a + b
About:
I've heard that overloading in Scala is not a good idea.
You can see Why "avoid method overloading"?
I'm trying to define, what I think to be a "default argument", with an implicit argument:
scala> def f(implicit x: Int, y: String) = y
f: (implicit x: Int, implicit y: String)String
Note - I tried to define implicit x: Int as the second argument, but I got a compile-time error:
scala> def f(y: String, implicit x: Int) = y
<console>:1: error: identifier expected but 'implicit' found.
def f(y: String, implicit x: Int) = y
Anyway, I define an implicit Int.
scala> implicit val x: Int = 100
x: Int = 100
Then I failed to invoke f with just a String argument:
scala> f("foo")
<console>:10: error: not enough arguments for method f:
(implicit x: Int, implicit y: String)String.
Unspecified value parameter y.
f("foo")
^
Next, I tried to invoke the f function by specifying the argument. That failed too.
scala> f(y = "foo")
<console>:10: error: not enough arguments for method f:
(implicit x: Int, implicit y: String)String.
Unspecified value parameter x.
f(y = "foo")
^
Is it possible to invoke the function f by providing an implicit and providing only a single argument for y?
Implicit parameter sections are all or nothing—either you explicitly pass every argument (assuming they don't have default values) or none of them. You need to do something like this if you want f("foo") to work:
def f(y: String)(implicit x: Int) = y
I'd be careful about this approach, though—I'm personally increasingly inclined to think that using implicits for anything except type class instances is a bad idea, and having an implicit Int value floating around is kind of scary.
Given
def add(x: Int, y: Int): Int = x + y
val addAsMethodReference: (Int, Int) => Int = add _
trait BinaryOperator {
def execute(x: Int, y: Int): Int
}
val addAsBinaryOperator: BinaryOperator = addAsMethodReference...?
How do I bind addAsMethodReference to BinaryOperator without implementing BinaryOperator by hand?
Java 8 SAM will just work. I could use the method reference anywhere binary operator trait is used in Java 8.
Ideally, I want to write something like:
var addAsBinaryOperator: BinaryOperator = addAsMethodReference.asNewInstanceOf[BinaryOperator]
The reason I want this asNewInstanceOf method is it would work for any method signature. I don't care how many parameters are being passed. If I had to implement this by hand I have to carefully match each x and y. This is error prone at a larger scale.
The specification of left.asNewInstanceOf[right] would be if the right side has more than one abstract method, it fails at compilation. If the left side is not a functional type that matches the single abstract method signature in the right side, it would fail at compilation. The right side doesn't need to be a trait, it could be an abstract class with a single abstract method.
Well, you could make the implicit conversion
implicit def myConversion(f: (Int, Int) ⇒ Int): BinaryOperator = new BinaryOperator {
def execute(x: Int, y: Int): Int = f(x, y)
}
and if it's in scope, you can just do
val addAsBinaryOperator: BinaryOperator = addAsMethodReference
for any binary function of integers returning an integer. Although maybe this also classifies as "implementing by hand". I can't see a way in which the compiler magically realizes that you want to interpret a function as an instance of a user-created trait with some particular structure.
NEW ANSWER:
This does what you want. It dynamically creates a BinaryOperator object whose execute method is bound to addAsMethodReference:
def add(x: Int, y: Int): Int = x + y
val addAsMethodReference: (Int, Int) => Int = add _
trait BinaryOperator {
def execute(x: Int, y: Int): Int
}
val addAsBinaryOperator: BinaryOperator =
new BinaryOperator{ def execute(x: Int, y: Int): Int = addAsMethodReference(x,y) }
OLD ANSWER
Is this what you want?
implicit class EnhancedBinaryOperator(val self: BinaryOperator) extends AnyVal {
def addAsMethodReference(a: Int, b: Int) = a + b
}
val o: BinaryOperator = ??? // anything here
o.addAsMethodReference(1,2) // addAsMethodReference is now on BinaryOperator
Could someone give a quick explanation why implicit conversion doesn't work in these cases? Thanks.
scala> implicit def strTwoInt (s: String):Int = s.toCharArray.map{_.asDigit}.sum
strTwoInt: (s: String)Int
scala> List[Int]("1","2","3") sum
res3: Int = 6
scala> List("1","2","3") sum
<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]
List("1","2","3") sum
scala> val a = List("1","2","3")
scala> a.foldLeft(0)((i:Int, j:Int) => i+j)
<console>:10: error: type mismatch;
found : (Int, Int) => Int
required: (Int, java.lang.String) => Int
Your implicit conversion converts a String in an Int. In your first example, it is triggered by the fact that you try to put Strings into a List of Ints.
In your second example, you have a List of String and you call the method sum, which takes an implicit Numeric[String]. Your conversion does not apply because you neither try to pass a String somewhere the compiler was expecting an Int, nor you tried to call a method which is defined in Int and not in String. In that case, you can either define a Numeric[String] which use explicitly your conversion, or use a method which takes a List[Int] as parameter (giving hint to the compiler):
scala> def sumIt( xs: List[Int] ) = xs sum
sumIt: (xs: List[Int])Int
scala> sumIt( List("1","2","3") )
res5: Int = 6
In the third example, the foldLeft second argument must be of type:
(Int,String) => Int
the one you actually passed is of type:
(Int,Int) => Int
however, you did not define any implicit conversion between these two types. But:
a.foldLeft(0)((i:Int, j:String) => i+j)
triggers your conversion and works.
Edit: Here's how to implement the Numeric[String]:
implicit object StringNumeric extends math.Numeric[String] {
val num = math.Numeric.IntIsIntegral
def plus(x: String, y: String) = num.plus(x,y).toString
def minus(x: String, y: String) = num.minus(x,y).toString
def times(x: String, y: String) = num.times(x,y).toString
def negate(x: String): String = num.negate(x).toString
def fromInt(x: Int) = x.toString
def toInt(x: String) = x
def toLong(x: String) = toInt(x)
def toFloat(x: String) = toInt(x)
def toDouble(x: String) = toInt(x)
def compare(x:String,y:String) = num.compare(x,y)
}
scala> List("1","2","3") sum
res1: java.lang.String = 6
It works, but the result is a String.
Here's a quick explanation: implicit conversions apply to the types the convert from and to directly (e.g., String and Int in your case), and not to any parametrized type T[String] or T[Int] — unless implicit conversions have been defined for T itself, and it is not the case for lists.
Your implicit conversion does not apply in your two cases (and even if you had an implicit conversion from List[String] to List[Int], it wouldn't apply). It would automatically applied only when you need a value of type Int and you're passing String instead. Here, in the first case, the method sum asks for a Numeric[String] implicit parameter — an implicit conversion from String to Int does not come into play here.
Similar problem for your next attempt: the foldLeft on your collection requires a function of type (Int, String) => Int. Imagine the mess we would get into if, based on an implicit conversion from String to Int, the compiler automatically provided an implicit conversion from (Int, Int) => Int to (Int, String) => Int…
For all these cases, the easy way to fix it is to explicitly call .map(stringToInt) on your collection beforehand.