Can anybody provide some details on <:<
operator in scala.
I think:
if(apple <:< fruit) //checks if apple is a subclass of fruit.
Are there any other explanations? I see many definitions in the scala source file.
Can anybody provide some details on <:<
operator in scala.
I think:
if(apple <:< fruit) //checks if apple is a subclass of fruit.
Are there any other explanations? I see many definitions in the scala source file.
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
Hmm... I can't seem to find "<:<" anywhere as well, but "<:" denotes subtyping. From http://jim-mcbeath.blogspot.com/2008/09/scala-syntax-primer.html#types :
List[T] forSome { type T <: Component }
In the above example, we are saying T is some type which is a subtype of Component.
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.
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}
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.