In the Twitter Effective Scala - Type Aliases, they say:
Don’t use subclassing when an alias will do.
trait SocketFactory extends (SocketAddress => Socket)
a SocketFactory is a function that produces a Socket. Using a type alias
type SocketFactory = SocketAddress => Socket
is better. We may now provide function literals for values of type SocketFactory and also use function composition: val addrToInet: SocketAddress => Long val inetToSocket: Long => Socket
val factory: SocketFactory = addrToInet andThen inetToSocket
Note that type aliases are not new types — they are equivalent to the syntactically substituting the aliased name for its type.
The sort of thing we're talking about is:
trait Base
trait T1 extends Base // subclassing
type T2 = Base // type alias
Obviously you can't use a type alias as a replacement when the class/trait has a body or stores information.
So using type aliases (T2) rather than extending with a trait or class (T1) has the following advantages:
However, it has the following disadvantages:
The fourth point is the most serious for me:
trait T1[T]
trait T2 extends T1[Any]
type T3 = T1[Any]
class C2 extends T2
val c = new C2
println("" + (c match { case t: T3 => "T3"; case _ => "any" }))
println("" + (c match { case t: T2 => "T2"; case _ => "any" }))
This produces:
T3
T2
The compiler gives a warning about the first pattern match, which clearly doesn't work as expected.
So, finally, the question. Are there any other advantages or disadvantages to using type aliases rather than extending a trait/class?
I think they key is actually that type aliases and traits are really different. The list of differences goes on and on and on:
x => x+7
would work as a type I2I = Int => Int
) and not traits.among others.
This is because you are doing dramatically different things in the two cases. Type aliases are simply a way to say, "Okay, when I type Foo, I actually mean Bar. They're the same. Got it? Cool." After you do this, you can substitute the name Foo
for Bar
wherever and whenever you feel like it. The only restriction is that once you decide what a type is you can't change your mind.
Traits, on the other hand, create an entirely new interface, which may expand on what the trait extends, or may not. If not, it's still a marker that this is its own type of entity, which can be pattern matched, tested for with 'isInstanceOf', and so on.
So, now that we've established that they're really different, the question is how to use each. And the answer is pretty simple: if you like the existing class just as it is except you dislike the name, use a type alias. If you want to create your own new entity that is distinct from other things, use a trait (or a subclass). The former is mostly for convenience, while the latter is for added type safety or capability. I don't think any rule saying use one instead of the other really captures the point--understand the features of both, and use each when those are the features you want.
(And then there's existential types, which provide similar capability to generics...but let's leave that for another question.)
They are different in that a type alias defines type equality relationship (ie. T1 <: T2 && T1 >: T2) while trait extension defines a strict sub-type relationship (ie. T1 <: T2 && !(T1 >: T2)). Use them wisely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With