Possible Duplicate:
What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?
I'm curious since I saw them in Scala library code, but I found it quite hard to Google something about them since their names are not words.
Possible Duplicate:
What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?
I'm curious since I saw them in Scala library code, but I found it quite hard to Google something about them since their names are not words.
These classes are used for implicit parameters that restrict the applicability of a method. Below is a description of each class. In general they are useful to restrain the a type parameter of an enclosing class within the context of a single method.
<:<[A,B]
or A <:< B
The compiler can provide an implicit instance of this type only when A is a subtype of B. This is similar to A <: B
in a type parameter list.
This can be useful when you want to put an additional constraint on a class type parameter in the context of a particular method. For example the class Foo
below can be used with any type, but the method bar
is only valid when T
is a subtype of Number
.
class Foo[T](x: T) {
// In general T could be any type
def bar(implicit ev: T <:< Number) = {
// This method can now only be used when T is a subtype of Number
// also, you can use ev to convert a T to number
ev(x).doubleValue
}
}
new Foo(123 : java.lang.Integer).bar // returns 123.0: Double
new Foo("123").bar // compile error: Cannot prove java.lang.String <:< java.lang.Number
=:=[A,B]
or A =:= B
The compiler can provide an implicit instance of this type only when A is the same type as B. This doesn't have an equivalent syntax in a type parameter list, you'd just use the same type parameter twice.
This can be used much like <:<
except that it requires the types to match exactly. This could be used to make a pair of methods mutually exclusive.
class Foo[T<:Number](x:T) {
def numOnly(implicit ev: T =:= Number) = ()
def floatOnly(implicit ev: T =:= Float) = ()
}
val asFloat = new Foo(123.0f:java.lang.Float)
asFloat.numOnly // Compile error
asFloat.floatOnly // Ok
val asNum = new Foo(123.0f:java.lang.Number)
asFloat.floatOnly // Ok
asFloat.numOnly // Compile error
Essentially if the type parameter is more specific than the constraint you can force the more specific method to be used.
<%<[A,B]
or A <%< B
The compiler can provide an implicit instance of this type only when A can be converted to B. This is similar to A <% B
in a type parameter list.
This requires that there is an implicit function available to turn an A into a B. This will always be possible when A <: B
since the implicit A <:< B
satisfies this constraint.
This class is actually marked as deprecated. It says you should instead just use A => B
.
<:<
, =:=
and <%<
are generic classes, all of them with two type parameters, and all of them extends Function1 (function with one argument). They are defined in Predef
.
They are intended to supply very basic conversion as implicits. The conversions are so basic that most of the time, they are identity. The point of having those classes and not Functions is that they may be distinguished from other functions that might be in implicit scopes. Predef gives the following implicit
A
, a <:<[A,A]
is available. As <:<
is [-From, +To]
,
<:<[A,A]
will satisfy any <:<[A,B]
where A
conforms to B
. The
implementation is identity. A
, there is also a =:=[A,A]
, but =:=
is invariant,
so it will not satisfy any A =:= B
if A is not exactly B.
implementation is identityA <%< B
each time A <% B
. Implementation is the
implicit conversion implied by A <% B
.The point is not to provide clever conversions (identity is not too clever), but to provide a library level way enforce some typing constraint at compile time, similar to language level constraint <:, <%, and simple lack of type argument, (which is quite a strong constraint too), in places where the language constraints are not available. A typical example is, in a generic class, when you want a method to be available only for some value of a type parameter. Suppose Collection[A], and I want a method sort that will be available only if A <: Ordered[A]. I don't want to put the constraint in the declaration of the class, because I want to accept collections whose elements are not Ordered. I just don't want them to have a sort
method. I cannot put the constraint on the method sort
either, because A
does not even appear as a type parameter of method sort. <:< provides a solution :
class MyCollection[A] {
def sort(implicit ev: A <:< Ordered[A])
// other methods, without constraints
}
Doing that, sort is technically available on all instances of MyCollection, but practically, when one does not pass ev parameter explicitely, it will look for an A <:< Ordered[A]
in implicit scope, and conforms
in predef will give one only if A
is a subtype of Ordered[A]
. As the (identity) conversion between A
and Ordered[A]
is made available in the implicit scope of the routine, A
will be usable as an Ordered[A]
in the body of method sort.
Using =:=
would force the type to be exactly Ordered[A] (the equivalent constraint on MyCollection
would be simply to make it non generic, and put the given type everywhere there is the generic parameter). <%<
would ensure there is an implicit conversion. That would be the more likely one here.
Actually for this particular method sort, the proper solution would be to get an Ordering[A]
in implicit context, with def sort(implicit ev: Ordering[A])
but that would not have demonstrated the point of those classes.
They are generalized type constraints for type arguments. Check out their definitions along with corresponding implicit conforms() methods in Predef.
In brief it works like this:
implicit tc: T <:< U
, that works like implicit tc: <:<[T, U]
implicit conforms()
method that returns required instance ensures that T <: U
(just like with regular generics)T <: U
is not the case, compilation of method call fails with "implicit not found"Look at usage sample here: http://java.dzone.com/articles/using-generalized-type