Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?

I can see in the API docs for Predef that they're subclasses of a generic function type (From) => To, but that's all it says. Um, what? Maybe there's documentation somewhere, but search engines don't handle "names" like "<:<" very well, so I haven't been able to find it.

Follow-up question: when should I use these funky symbols/classes, and why?

like image 356
Jeff Avatar asked Aug 06 '10 20:08

Jeff


People also ask

What does :_* mean in Scala?

: _* is a special instance of type ascription which tells the compiler to treat a single argument of a sequence type as a variable argument sequence, i.e. varargs.

What does ++ mean in Scala?

In this case it means "add these to the end." Also note that if a class defines ++ but not ++= then the compiler will treat x ++= y. as x = x ++ y. this is true generally for symbols ending in an equal sign (other than == , != , and = , of course).

What is some function in Scala?

Scala some class returns some value if the object is not null, it is the child class of option. Basically, the option is a data structure which means it can return some value or None. The option has two cases with it, None and Some. We can use this with the collection.


1 Answers

These are called generalized type constraints. They allow you, from within a type-parameterized class or trait, to further constrain one of its type parameters. Here's an example:

case class Foo[A](a:A) { // 'A' can be substituted with any type     // getStringLength can only be used if this is a Foo[String]     def getStringLength(implicit evidence: A =:= String) = a.length } 

The implicit argument evidence is supplied by the compiler, iff A is String. You can think of it as a proof that A is String--the argument itself isn't important, only knowing that it exists. [edit: well, technically it actually is important because it represents an implicit conversion from A to String, which is what allows you to call a.length and not have the compiler yell at you]

Now I can use it like so:

scala> Foo("blah").getStringLength res6: Int = 4 

But if I tried use it with a Foo containing something other than a String:

scala> Foo(123).getStringLength <console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String] 

You can read that error as "could not find evidence that Int == String"... that's as it should be! getStringLength is imposing further restrictions on the type of A than what Foo in general requires; namely, you can only invoke getStringLength on a Foo[String]. This constraint is enforced at compile-time, which is cool!

<:< and <%< work similarly, but with slight variations:

  • A =:= B means A must be exactly B
  • A <:< B means A must be a subtype of B (analogous to the simple type constraint <:)
  • A <%< B means A must be viewable as B, possibly via implicit conversion (analogous to the simple type constraint <%)

This snippet by @retronym is a good explanation of how this sort of thing used to be accomplished and how generalized type constraints make it easier now.

ADDENDUM

To answer your follow-up question, admittedly the example I gave is pretty contrived and not obviously useful. But imagine using it to define something like a List.sumInts method, which adds up a list of integers. You don't want to allow this method to be invoked on any old List, just a List[Int]. However the List type constructor can't be so constrainted; you still want to be able to have lists of strings, foos, bars, and whatnots. So by placing a generalized type constraint on sumInts, you can ensure that just that method has an additional constraint that it can only be used on a List[Int]. Essentially you're writing special-case code for certain kinds of lists.

like image 91
Tom Crockett Avatar answered Sep 30 '22 12:09

Tom Crockett