Skip to content

Instantly share code, notes, and snippets.

@micrypt
Created April 13, 2011 13:45
Show Gist options
  • Save micrypt/917569 to your computer and use it in GitHub Desktop.
Save micrypt/917569 to your computer and use it in GitHub Desktop.
For what it's worth, my experience learning Scala's type system was:
- plain old (nominal) types: easy, same as Java
- type parameterization: also easy, same as Java, but with square brackets instead of angle brackets. I felt square brackets looked a lot nicer, and probably was therefore easier to read, too.
- function types: functions in Scala have nominal types like anything else, with a type parameter for the result type and one each for the parameter types. But there's also a shorthand syntax. Instead of Function1[String, Int], for example, you can say String => Int. The latter is actually easier to read, but when combined with an assignment such as "val a: String => Int = something I find it can sometimes be non-obvious at first glance how to parse it.
- traits: traits were quite easy for me to understand and use. They are exactly like classes except they can't (currently) take constructor parameters and the meaning of super is different inside them. Mixing in multiple traits is straightforward, as it is like implementing multiple interfaces in Java, except traits can have concrete method implementations and real fields. Self types inside traits was something new, but made sense and was quite easy to grok, and are quite powerful.
- singleton objects: these were also quite easy to get, as they are really just a shorthand for the Gang of Four singleton pattern, and can also be seen a more object-oriented way to do statics. The type of a singleton object is its name followed by ".type". Almost never need to use that, but it is pretty straightforward and easy to understand. The SingletonObject.type refers to a synthetically generated class name for the singleton object, which has dollar signs in its name in bytecodes.
- variance: As I mentioned earlier, I found nonvariance and contravariance quite intuitive, but contravariance to be counterintuitive. That latter one took a bit of study, but with a bit of study it made sense. It wasn't that difficult. Variance also includes the concept of upper and lower bounds, which made sense to me. I found the concept of upper and lower bounds easy, but I still have trouble remembering which symbol goes to which: >: means lower bounds; <: means upper bounds. Maybe a good mnemonic is that the one that looks like a smiley face is on uppers.
- view bounds: I did find implicit parameters a bit mysterious at first, but once it I saw good examples of its use I realized what it was for and found it quite sensible. View bounds is a shorthand for a very powerful use of implicit parameters. Its symbol is <%. So it looks like upper bounds except with a percent sign instead of a colon. It is similar conceptually to upper bounds too, except instead of a requirement that something is a subtype, it means something must be implicitly convertible to a type. I think this one can be tricky to learn and understand, so we worked hard on the example in the book to make it as easy to learn as possible. I have used it in ScalaTest and it was very helpful at making the library easier to use.
- existential types: this one's in there primarily for compatibility with Java. Even though one of the things it's supposed to do is enable expression of wildcard types, I found it pretty easy to understand. An existential type says that a type exists, but I don't know exactly what it is. I may know something about it, such that it is a subtype of some other type, and the way that is written is pretty readable. As I mentioned before, what I've found difficult is knowing how to write the Scala existential type that maps to particular a Java wildcard types. There's a shorthand for existential types that involves underscores, but I also found this pretty intuitive. The underscore is Scala is used for a number of different things, but generally stands for something that's left out. In this case, it's a type that's left out, and I found it quite easy to read.
- structural types: this one was also quite easy for me to understand. It is basically compile-time checked duck typing. The syntax is pretty obvious too. I've used it in ScalaTest, and it helped make the API much easier to write and use. Sometimes duck typing is exactly what's needed.
- higher-kinded types: I've never used it to my knowledge. It's syntax is so natural, though, that I may have used it without realizing that's what I was using. This one is not covered in the book. Martin is planning to reimplement Scala's collections library using higher-kinded types for the next release, and says it will eliminate code duplication in the collections library implementation.
- path-dependent types: this one I found a bit mysterious at first. I'm still not sure I appreciate fully its potential uses, but the mechanics of it is quite straightforward. A "path" is some way to get to an object at runtime, and the type at the end is a type defined inside that object's class. So a path dependent type to an inner class instance would be a different type for each different outer class. One place it is used in Scala is in the Enumeration library class.
- abstract types: Scala classes can have type members as well as field and method members, and like fields and methods, they can be abstract. This actually all made sense to me, and seemed quite consistent. What is still not clear to me is how to explain when to use type parameters versus abstract type members. (The book is fairly silent on that subject.) There's some overlap between them, but there are differences also. One difference is you can't use variance with abstract types, just with type parameters. With more experience designing libraries I expect I'll discover more insights, but for now I generally reach first for type parameters.
- type inference: This was also quite easy. The compile will generally infer types where you think it can infer types. On rare occasions you may get a compiler error that clearly says it can't infer the type, and you just fill it in.
That's all I can think of that could be called "Scala's type system". As you can see there are a lot of pieces to it. But with a few exceptions I found most of the pieces to be quite easy to learn and understand. So the challenge in learning it is probably more in how many different pieces there are to learn than in the difficulty of the individual pieces themselves.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment