4

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.

Community
  • 1
  • 1
Karel Smutný
  • 1,100
  • 1
  • 9
  • 17
  • 5
    It would be really helpful if Stack Overflow didn't barf on search operators. It's all but impossible to search for questions regarding these -- the one linked is just of of many. – Daniel C. Sobral Aug 04 '11 at 22:10

3 Answers3

10

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.

Geoff Reedy
  • 34,891
  • 3
  • 56
  • 79
4

<:<, =:= 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

  • For every type 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.
  • For every type 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 identity
  • There is also a A <%< 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.

Didier Dupont
  • 29,398
  • 7
  • 71
  • 90
1

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:

  • you add implicit parameter to your method like implicit tc: T <:< U, that works like implicit tc: <:<[T, U]
  • there is implicit conforms() method that returns required instance ensures that T <: U (just like with regular generics)
  • if 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

tuxSlayer
  • 2,804
  • 2
  • 20
  • 24