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"?
Related
def foo(implicit i: Int = 1) = i
def bar(implicit j: Int) = foo()
bar(4)
This piece of code evaluates to 1. So the default value has priority over the implicit j, which is instantiated to 4. Therefore, it seems that, at least in this example, the default parameter value trumps implicit, making the definition of foo equivalent to def foo(i: Int = 1) = i.
Do default parameter values always trump implicits? If yes, why is this code legal (given that it is confusing)? If not, what is a counter-example?
Is there a way to get the other behavior, i.e. that a piece of code similar to the above (with a default value for i) would evaluate to 4, without having to pass the argument explicitly?
implicit is applied to the entire parameter list, not just a single parameter (e.g., you can have foo(implicit i: Int, j: Int), and both parameters are implicit, but, if you wanted only one of them to be, you'd have to split them in two lists: def foo(i: Int)(implicit j: Int).
So, to pass implicit parameters to the function, you have to omit the entire list: foo, not foo().
When you have def foo(implicit i: Int), foo() does not even compile, because you are trying to send an empty parameter to list. foo does (as long as an implicit int is in scope), because the list is passed implicitly.
With def foo(implicit i: Int = 1), both uses compile, but mean different things. foo() means "call foo with default values for all parameters", foo means "call foo, passing the implicit parameter list".
So, with bar(implicit j: Int) = foo it will evaluate to the value of j, while bar(implicit j: Int) = foo() evaluates to 1.
Scala compiler gets confused between implicit and default value. foo() makes the compiler to ignore implicit. so just foo does the trick here
def foo(implicit i: Int = 1) = i
def bar(implicit j: Int) = foo
println(bar(5))
Result : 5
If I wanted to make an add method that adds values of two different types like:
add[T,S](op1: T, op2: S): ? = ...
The most notable example I can think of are the basic number types. If I add a Byte and an Int then it would return an Int. If two Bytes are added it could return an Int depending on if the -127 to 128 limit for the Byte is broken.
Further more if I wanted to create classes that had the same characteristics I would like it to do the same.
One possible solution is to have classes extend the same trait or class. But for the example of Scala primitive types this doesn't apply cause Int, Double, Float, Byte don't share a common ancestor besides Any.
I've also looked at the Numeric[T] trait but that doesn't seem to help when adding different primitive types.
Thanks
Kai
This is a prime example for a some kind of joint-typeclass.
Start by defining a thing just like Numeric[T], but for addability:
trait Addable[T, S] {
type Result
def add(x: T, y: S): Result
}
Now, define your add function:
def add[T,S](op1: T, op2: S)(implicit op: Addable[T, S]): op.Result =
op.add(op1, op2)
All you have to do left, is to create implicit instances for Addable:
// Put them in the companion object, so the implicits are available
// without explicit import
object Addable {
// Make any numeric addable
implicit def numericAddable[T : Numeric]: Addable[T, T] = {
new Addable[T, T] {
type Result = T
def add(x: T, y: T): T = {
val op = implicitly[Numeric[T]]
op.plus(x, y)
}
}
}
}
But you now can also define your own classes and define (assymetric) addition capability:
case class A(x: Int)
case class B(x: Int)
case class C(x: Int)
implicit object AddABC extends Addable[A,B] {
type Result = C
def add(x: A, y: B): C = C(x.x + y.x)
}
This will allow you to write:
add(A(1), B(2))
However, any of these will fail at compile time:
add(A(1), A(2))
add(B(1), A(2))
add(B(1), B(2))
Unfortunately, this does not work for weak conforming numeric types:
add(1, 1) // compiles
add(1.0, 1.0) // compiles
add(1, 1.0) // fails to compile
Judging from another post, there is not really another way to achieve that, than to define the cases manually (with some helper methods of course).
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
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
I want to create a method sum that I can call on different types, specifically sum(1,2).
def sum[A](a1: A, a2: A) = a1 + a2
This fails because the compiler can't tell if A has a method '+'
I tried to define a structural type:
type Addable = {def +(a: Addable)}
This fails because of an illegal cyclic reference
How can I achieve this in a type safe way without requiring A to extend a specific trait?
Scala does not support recursive type aliases without additional compiler arguments (specifically, -Yrecursion). This is partially to keep the type checker at least somewhat in the realm of decidability (though, as we have discovered, the type system is Turing Complete even without recursive type aliases, so it doesn't much matter).
The correct way to do this sort of thing is with a typeclass. Scala encodes these as implicit view bounds. For example:
trait Addable[A] {
def zero: A
def add(x: A, y: A): A
}
implicit object IntAddable extends Addable[Int] {
def zero = 0
def add(x: Int, y: Int) = x + y
}
implicit object DoubleAddable extends Addable[Double] {
def zero = 0
def add(x: Double, y: Double) = x + y
}
// ...
def sum[A](x: A, y: A)(implicit tc: Addable[A]) = tc.add(x, y)
And of course, this also allows you to do fancy things like sum the contents of a Seq in a type-safe manner:
implicit def summableSeqSyntax[A](seq: Seq[A])(implicit tc: Addable[A]) = new {
def sum = seq.foldLeft(tc.zero)(tc.add)
}
List(1, 2, 3, 4).sum // => 10
List(true, false).sum // does not compile
It is worth noting that Scala 2.8 has something very close to this with the Numeric typeclass.