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
Related
This code doesn't work!
def add(a: Int, b: Int): Int = a plus b
So, I tried defining plus like so:
def plus(a: Int, b: Int): Int = a + b
But, the compiler still complains Cannot resolve symbol plus!
Any ideas?
a plus b doesn't work because it is shorthand for a.plus(b) and there is no such method on Int in the standard library. To make that work you have to "enhance" the Int class via implicit conversion.
implicit class MyPlus[T](a: T)(implicit ev:Numeric[T]) {
def plus(b: T): T = ev.plus(a,b)
}
Now you can do 3 plus 5 or a plus b etc.
You can also do it like this (a little more concise and readable but essentially the same thing):
import Numeric.Implicits._
implicit class MyPlus[T: Numeric](a: T) {
def plus(b: T): T = a + b
}
Since a inside add is an Int, and scala.Int doesn't have a plus method, we need to create an implicit conversion from scala.Int to something which wraps an Int and has a plus method:
implicit class IntWithPlus(val i: Int) extends AnyVal {
def plus(other: Int) = i + other
}
Now, everything just works:
def add(a: Int, b: Int): Int = a plus b
add(2, 3)
// => 5
An alternative would be to create a type Int that gets imported into the local namespace shadowing scala.Int and give that type a plus method.
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"?
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).
Can't figure out what's wrong with this code:
trait NumberLike
{
def plus[T](a: T, b: T): T
}
class IntegerNumberLike extends NumberLike
{
def plus[Int](a: Int, b: Int): Int = 2 // type mismatch; found : scala.Int(2) required: Int
}
But if I do it this way, it works:
trait NumberLike[T]
{
def plus(a: T, b: T): T
}
class IntegerNumberLike extends NumberLike[Int]
{
def plus(a: Int, b: Int): Int = 2
}
So I got two questions:
Why first code sample doesn't work?
Generally, when should I use class type parameter and when should I use method type parameter?
Type parameters in methods are much like other parameters, the name of actual parameter isn't significant: plus[Int](a: Int, b: Int): Int is exactly the same as plus[T](a: T, b: T): T
Now, it is easy to understand why plus[T](a: T, b: T): T = 2 does not compile, isn't it? Because 2 is not of type T.
As to your second question, it is hard to answer exactly, because it is rather broad. In a nutshell, parametrized classes and methods define a template of a class or a method respectively. Think of it as a family of classes/methods rather than a single object. For example, instead of plus[T] (a: T, b: T): T, one could have written:
def plus(a: Int, b: Int): Int
def plus(a: Long, b: Long): Long
def plus(a: Double, b: Double): Double
etc.
Or, instead of class NumberLike[T], you could have:
class IntLike
class LongLike
class DoubleLike
etc.
Looking at it this way, you can ask yourself a question, what it is you are designing: is it a family of classes or a family of methods? Answering that question will tell you whether you should parametrize a class, a method, or, perhaps, both ... Consider:
class Iterable[+A] {
...
def def reduceLeft[B >: A](op: (B, A) ⇒ B): B
...
}
The definition:
def plus[Int](a: Int, b: Int): Int
is equivalent to
def plus[T](a: T, b: T): T
For example, you could see it more clearly through the following example:
type Int = String
def main(args: Array[String]) {
val x: Int = "foo"
println(x)
}
where you get no errors and "foo" printed. Since you are only renaming your parametric type as Int (instead of T). That's why the compiler complains that Int (in this case the value of 2) and Int (your parametric type name) are not the same. I don't know if you can implement the plus function for specific types out of the parametric definition. You use class type parameters if the parameters apply to all the class, and method parameters if the parameters apply only to that method. It's just a matter of visibility and responsibility of the methods.
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.