a.⊗(b)
, so we have to add a $\otimes$ method to class Int
. However, we cannot do this easily because Int
is defined in the core Scala library. This situation is where Scala's implicit conversion can come to play: an implicit class defined as follows would perfectly serve our purpose:implicit class Converter(b: Double) { def ⊗(a: Int): Boolean = F(a, b) }Note that the class name doesn't matter. The point is to use the keyword
implicit
and provide the function $\otimes$ with the correct signature. Now, when the compiler encounters something like a⊗b
and tries to resolve the method call, it does not find a method named "$\otimes$" in class Int
. It will then look for any implicit conversions it could use to transform a
into something that has a method named "$\otimes$" with an applicable signature. Assuming our Converter
class is in the right scope, the compiler will implicitly construct a Converter
object from a
and call $\otimes$ on that object instead.PS. Scala defines a default converter from type
Any
to type String
, which simply invokes the toString
method of Any
.Type classes. Suppose we have a function $F: A \rightarrow B$ and we would like it to work only for a restricted subset $A'$ of $A$. There is no way to express such type constraint through inheritance. In Scala, however, we can use the type class pattern to set the constraint. Here is the idea: 1) create an abstract class that will represent a type class, 2) create elements representing the classes belonging to that type class, and 3) use an implicit parameter to restrict the type parameter of $F$ to that type class.
More concretely, suppose $A$ is the
Numeric
type and we would like $F$ to work only for Int
and Long
, but not any other subtype of Numeric
. We first define an abstract type class:abstract class Acceptable[T]Next, we create the members of the type class, one for
Int
and another for Long
. To make them available without need for import statements, we put them inside the companion class to Acceptable.object Acceptable {
implicit object AllowInt extends Acceptable[Int]
implicit object AllowLong extends Acceptable[Long]
}
Finally, we allow $F$ to accept only type Int and Long by letting it take an implicit parameter:
def F[T](t: T)(implicit constraint: Acceptable[T]) = ...Now, if you try to call $F(x)$ on an argument $x$ of type other than
Int
and Long
, you will get an error at compile time, because the compiler cannot find the implicit object for that type. You can increase the number of allowed types by defining new implicit objects in the right scope.
No comments:
Post a Comment