but first
You can do
def func(x: Int, y: Int): Int = x * y
or
def func(x: Int)(y: Int): Int = x * y
Or
def func(a: Int, b: Int, c: Int, d: Int, e: Int) = ???
or
def func(a: Int)(b: Int)(c: Int)(d: Int)(e: Int) = ???
or
def func(a: Int, b: Int, c: Int)(d: Int, e: Int) = ???
You can divide up your arguments into separate groups.
def func(x: Int)(y: Int): x * y
func(3)(4)
res1: Int = 12
func(3)
error: missing arguments for method func
implicit val a: Int = 2
def func(x: Int)(implicit y: Int) = x * y
func(3)(4)
res1: Int = 12
func(3)
res2: Int = 6
If a parameter is marked implicit, if you leave it out the compiler will look for a value of that type marked as implicit and insert it for you.
func(3)
becomes
func(3)(2)
Implicit parameters go in the last parameter list
def func(x: Int)(implicit y: Int, h: Helper) = ...
Both y and h are implicit
Implicits need to be unambiguous.
implicit val a: Int = 2
implicit val b: Int = 99
def func(x: Int)(implicit y: Int) = x * y
// error: ambiguous implicit values:
def func1(x: String)(implicit h: Helper) = {
h.help(x)
}
def func2(a: Int, b:String)(implicit h: Helper) = {
func1(b)
...
}
implicit def arbitraryStringToInt(str: String): Int = str.length * 10
def func(x: Int): Int = x + 1
func(5)
res1: Int = 6
func("cat")
res2: Int = 31
If you call a method with a parameter of the wrong type, the compiler will try and find an implicit conversion from the type you are providing to the type expected by the method, and insert the call to that function.
func("cat")
becomes
func(arbitraryStringToInt("cat"))
implicit def arbitraryStringToInt(str: String): Int = ...
Don't actually do this!
What if wanted to do this?
5.isEven
res1: Boolean = false
Or
6 times 7
res2: Int = 42
class SuperInt(x: Int) {
def isEven: Boolean = x % 2 == 0
}
implicit def intToSuperInt(x: Int): SuperInt = new SuperInt(x)
5.isEven
res1: Boolean = false
If you invoke a method on an object that doesn't have that method, the compiler looks for an implicit conversion from that obect type to one that does have the method.
implicit class SuperInt(x: Int) {
def isEven: Boolean = x % 2 == 0
def isOdd: Boolean = !isEven
def times(y: Int): Int = x * y
}
7.isOdd
res1: Boolean = true
6 times 7
res2: Int = 42
3.times(4)
res3: Int = 12
If you invoke a method on an object that doesn't have that method, the compiler looks for an implicit class that has the method.
Lots of syntactic sugar/helpers in scala are implemented this way. See Predef for examples.
1 to 5 = Range(1, 2, 3, 4, 5)
from implicit def intWrapper(x: Int): RichInt
"cats".map(_.toUpper) = "CATS"
from implicit def augmentString(x: String): StringOps
trait Feature[T] {
def impl(x: T): ??
}
implicit object IntFeature extends Feature[Int] {
def impl(x: Int) : ???
}
trait Feature[T] {
def impl(x: T): Unit
}
implicit object IntFeature extends Feature[Int] {
def impl(x: Int) = println(s"int feature for $x")
}
or
implicit val intFeature = new Feature[Int] {...}
val intF = implicitly[Feature[Int]]
intF.impl(5) // int feature for 5
case class MyClass(i: Int, s: String)
implicit object MyClassFeature extends Feature[MyClass] {
def impl(x: MyClass) = println(s"feature for MyClass ${x.i}, ${x.s}")
}
def usesFeature[T](x: T)(implicit evidence: Feature[T]) = evidence.impl(x)
usesFeature(5)
// feature for 5
usesFeature(MyClass(9, "bananas"))
// feature for MyClass 9 bananas
On the basis of the type we call usesFeature with, compiler finds the right implicit value.
Evidence? The existence of an implicit value of type Feature[T] provides evidence that we can provide the feature for type T...
usesFeature(MyClass(9, "bananas"))
// feature for MyClass 9 bananas
usesFeature(List(1,2))
// error: could not find implicit value for parameter evidence: Feature[List[Int]]
Or provide the implicit class so you can call the feature directly
implicit class FeatureOps[T : Feature](x: T) {
def impl = implicitly[Feature[T]].impl(x)
}
8.impl
// feature for 8
def checkFeature[T : Feature](x: T): T = x
checkFeature(7)
res1: Int = 7
checkFeature(9.99)
// error: cound not find implicit value for evidence parameter of type Feature[Double]
def checkFeature[T : Feature](x: T): T = x
Is just syntactic sugar for
def checkFeature[T](x: T)(implicit ev: Feature[T]): T = x
An example: sorting.
List(2,4,3,5).sorted
res1: List[Int] = List(2,3,4,5)
case class Things(x: Int, y: String)
List(Things(2, "2"), Things(4, "5"), Things(2,"3")).sorted
//error: No implicit Ordering defined for Things
in scala.collection.immutable.List
def sorted[B >: A](implicit ord: math.Ordering[B]): List[A]
implicit object ThingsOrdering extends math.Ordering[Things] {
def compare(a: Things, b: Things): Int =
if ( (a.x compare b.x) == 0)
a.y compare b.y
else
a.x compare b.x
}
List(Things(2, "2"), Things(4, "5"), Things(2,"3")).sorted
res1: List[Things] = List(Things(2,2), Things(2,3), Things(4,5))
Retroactive extension: conforming to an interface without access to source code.
Separation of data and behavior...