Can anybody provide some details on <:<
operator in scala.
I think:
if(apple <:< fruit) //checks if apple is a subclass of fruit.
The <:<
type is defined in Predef.scala along with the related types =:=
and <%<
as follows:
// used, for example, in the encoding of generalized constraints
// we need a new type constructor `<:<` and evidence `conforms`, as
// reusing `Function2` and `identity` leads to ambiguities (any2stringadd is inferred)
// to constrain any abstract type T that's in scope in a method's argument list (not just the method's own type parameters)
// simply add an implicit argument of type `T <:< U`, where U is the required upper bound (for lower-bounds, use: `U <: T`)
// in part contributed by Jason Zaugg
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x} // not in the <:< companion object because it is also intended to subsume identity (which is no longer implicit)
This uses the Scala feature that a generic type op[T1, T2]
can be written T1 op T2
. This can be used, as noted by aioobe, to provide an evidence parameter for methods that only apply to some instances of a generic type (the example given is the toMap
method that can only be used on a Traversable
of Tuple2
). As noted in the comment, this generalizes a normal generic type constraint to allow it to refer to any in-scope abstract type/type parameter. Using this (implicit ev : T1 <:< T2
) has the advantage over simply using an evidence parameter like (implicit ev: T1 => T2
) in that the latter can lead to unintended in-scope implicit values being used for the conversion.
I'm sure I'd seen some discussion on this on one of the Scala mailing lists, but can't find it at the moment.
To better understand the implementation.
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x}
I tried to devise a simpler implementation. The following did not work.
sealed class <:<[-From <: To, +To]
implicit def conforms[A <: B, B]: A <:< B = new (A <:< B)
At least because it won't type check in all valid use cases.
case class L[+A]( elem: A )
{
def contains[B](x: B)(implicit ev: A <:< B) = elem == x
}
error: type arguments [A,B] do not conform to class <:<'s
type parameter bounds [-From <: To,+To]
def contains[B](x: B)(implicit ev: A <:< B) = elem == x
^
Actually, it checks if the class represented by the Manifest
apple is a subclass of the class represented by the manifest fruit.
For instance:
manifest[java.util.List[String]] <:< manifest[java.util.ArrayList[String]] == false
manifest[java.util.ArrayList[String]] <:< manifest[java.util.List[String]] == true
<:<
is not an operator - it is an identifier and is therefore one of:
In this case, <:<
appears twice in the library, once in Predef
as a class and once as a method on Manifest
.
For the method on Manifest
, it checks whether the type represented by this manifest is a subtype of that represented by the manifest argument.
For the type in Predef
, this is relatively new and I am also slightly confused about it because it seems to be part of a triumvirate of identical declarations!
class <%<[-From, +To] extends (From) ⇒ To
class <:<[-From, +To] extends (From) ⇒ To
class =:=[From, To] extends (From) ⇒ To
Copy from scala.Predef.scala:
// Type Constraints --------------------------------------------------------------
// used, for example, in the encoding of generalized constraints
// we need a new type constructor `<:<` and evidence `conforms`, as
// reusing `Function2` and `identity` leads to ambiguities (any2stringadd is inferred)
// to constrain any abstract type T that's in scope in a method's argument list (not just the method's own type parameters)
// simply add an implicit argument of type `T <:< U`, where U is the required upper bound (for lower-bounds, use: `U <: T`)
// in part contributed by Jason Zaugg
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x}
I asked around, and this is the explanation I got:
<:<
is typically used as an evidence parameter. For example in TraversableOnce
, toMap
is declared as def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U]
. This expresses the constraint that the toMap
method only works if the traversable contains 2-tuples. flatten
is another example. <:<
is used to express the constraint that you can only flatten a traversable of traversables.