Dev Dives: Streamline document processing with UiPath Studio Web
Implicit conversion and parameters
1. Implicit Conversion and Parameters in Scala
Piyush Mishra
Software Consultant
Knoldus software LLP
2. Topics Covered
Why Implicit are needed
Implicit conversions
Rule for Implicit
Where Implicit are tried
Implicit Parameters
Debugging Implicit
3. Why Implicit are needed
There’s a fundamental difference between your own code and libraries of other
people
you can change or extend your own code as you wish
but if you want to use someone else’s libraries, you usually have to take them
as they are.
They are also used in resolving type check errors.
So in order to use others library as yours you use implicit conversions and
parameters
4. Implicit Conversion
One of the central collection traits in Scala is
RandomAccessSeq[T], which describes random
access sequences over ele-
ments of type T.
Java’s String class does not inherit from Scala’s
RandomAccessSeq trait.
5. Implicit Conversion
In situations like this, implicits can help. To make a String appear to be
a subtype of RandomAccessSeq, you can define an implicit conversion
from String to an adapter class that actually is a subtype of
RandomAccessSeq
implicit def stringWrapper(s: String) =
new RandomAccessSeq[Char] {
def length = s.length
def apply(i: Int) = s.charAt(i)
}
scala> stringWrapper("abc123") exists (_.isDigit)
res0: Boolean = true
6. Rules For Implicit
Implicit definitions are those that the compiler is
allowed to insert into a program in order to fix
any of its type errors .
Implicit conversions are governed by the
following general rules.
7. Marking Rule
Only definitions marked implicit are available. The implicit keyword is
used to mark which declarations the compiler may
use as implicits.
we can use it to mark any variable, function, or object
Definition.
implicit def intToString(x: Int) = x.toString
The compiler will only change x + y
convert(x) + y if convert is marked as implicit. The compiler will only
select among the definitions you have explicitly marked as implicit.
8. Scope Rule
An inserted implicit conversion must be in
scope as a single identifier, or be associated
with the source or target type of the conversion
The Scala compiler will only consider implicit
conversions that are in scope. To make an
implicit conversion available, therefore, you
must in some way bring it into scope
9. Non-Ambiguity Rule
An implicit conversion is only inserted if there
is no other possible conversion to insert.
If the compiler has two options to fix x + y, say
using either convert1(x) + y or convert2(x) + y,
then it will report an error and refuse to choose
between them.
10. Explicits-First Rule
Whenever code type checks as it is written, no
implicits are attempted.
The compiler will not change code that already
works.
11. One-at-a-time Rule
Only one implicit is tried. The
compiler will never rewrite x + y to
convert1(convert2(x)) + y.
Doing so would cause compile times
to increase dramatically on erroneous
code .
12. Naming an implicit conversion.
object MyConversions {
implicit def stringWrapper(s: String):
RandomAccessSeq[Char] = ...
implicit def intToString(x: Int): String = ...
}
import MyConversions.stringWrapper
... // code making use of stringWrapper
In this example, it was important that the implicit conversions
had names,
because only that way could you selectively import one and
not the other.
13. Where implicits are tried
There are three places implicits are used in the
Language
conversions to an expected type.
conversions of the receiver of a selection.
implicit parameters.
14. Implicit conversion to an expected
type
def printWithSpaces(seq: RandomAccessSeq[Char]) = seq mkString " "
println(printWithSpaces("Hello"))
15. Converting the receiver
suppose you write down obj.doIt, and obj does
not have a member named doIt. The compiler will try to insert
conversions
before giving up. In this case, the conversion needs to apply to the
receiver,
obj. The compiler will act as if the expected “type” of obj were “has a
member named doIt.
16. Interoperable with new types
class Rational(n: Int, d: Int) {
def + (that: Rational): Rational = ...
def + (that: Int): Rational = ...
}
scala> 1 + oneHalf
<console>:6: error: overloaded method value + with
alternatives (Double)Double <and> ... cannot be applied
to (Rational)
1 + oneHalf
scala> implicit def intToRational(x: Int) =
new Rational(x, 1)
intToRational: (Int)Rational
scala> 1 + oneHalf
res6: Rational = 3/2
●
17. Simulating new syntax
The other major use of implicit conversions is to
simulate adding new syntax.
Recall that you can make a Map using syntax like this:
Map(1 -> "one", 2 -> "two", 3 -> "three")
Have you wondered how the -> is supported? It’s not
syntax! Instead, -> is
a method of the class ArrowAssoc, a class defined
inside the standard Scala preamble (scala.Predef)
18. Implicit parameters
The remaining place the compiler inserts implicits is within argument
lists.
The compiler will sometimes replace someCall(a) with someCall(a)
(b),
or new SomeClass(a) with new SomeClass(a)(b), thereby adding a
missing parameter list to complete a function call.
For example, if someCall’s missing last parameter list takes three
parameters, the compiler will supply them implicitly
19. Debugging implicits
Sometimes you might wonder why the compiler did not find an implicit
conversion that you think should apply. In that case it helps to write the
conversion out explicitly. If that also gives an error message, you then know
why the compiler could not apply your implicit
scala> val chars: List[Char] = "xyz"
error: type mismatch; java.lang.String("xyz") required: List[Char]
scala> val chars: List[Char] = stringWrapper("xyz")
error: type mismatch;
found java.lang.Object with RandomAccessSeq[Char]
required: List[Char]
val chars: List[Char] = stringWrapper("xyz")