The Artima Developer Community
Sponsored Link

Weblogs Forum
Abstract Type Members versus Generic Type Parameters in Scala

12 replies on 1 page. Most recent reply: Nov 2, 2009 6:47 AM by Howard Lovatt

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 12 replies on 1 page
Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Abstract Type Members versus Generic Type Parameters in Scala (View in Weblogs)
Posted: Oct 6, 2009 11:44 PM
Reply to this message Reply
Summary
In this blog, I attempt to answer a common question among Scala programmers: when would I opt to use an abstract type member instead of a generic type parameter in Scala API design?
Advertisement

A common question asked by people learning Scala is how to decide between abstract type members and generic type parameters when you need to model an abstract type. For those unfamiliar with the difference, type parameters in Scala are similar to type parameters in Java, except instead of Java's angle brackets:

interface Collection<T> {
    // ...
}

Scala uses square brackets:

// Type parameter version
trait Collection[T] {
  // ...
}

Abstract type members in Scala have no equivalent in Java. In both languages, classes, interfaces (in Java), and traits (in Scala) can have methods and fields as members. In Scala, a class or trait can also have type members, and just as methods can be abstract in Java, methods fields, and types can all be abstract in Scala. Here's how an abstract type member looks in Scala:

// Type member version
trait Collection {
  type T
  // ...
}

In both cases, the abstract type can be made concrete in a subtype. Here's a subtype of the type parameter version of Collection that specifies the concrete type String for T:

// Type parameter version
trait StringCollection extends Collection[String] {
  // ...
}

Here's a subtype of the type member version of Collection that also specifies the concrete type String for T:

// Type member version
trait StringCollection extends Collection {
  type T = String
  // ...
}

This seems to be, and in fact is, two different ways to achieve the same goal. So when would you choose one over the other? I asked Martin Odersky this question in an interview. He first explained that one reason for having both abstract type members and generic type parameters is orthogonality:

There have always been two notions of abstraction: parameterization and abstract members. In Java you also have both, but it depends on what you are abstracting over. In Java you have abstract methods, but you can't pass a method as a parameter. You don't have abstract fields, but you can pass a value as a parameter. And similarly you don't have abstract type members, but you can specify a type as a parameter. So in Java you also have all three of these, but there's a distinction about what abstraction principle you can use for what kinds of things. And you could argue that this distinction is fairly arbitrary.

What we did in Scala was try to be more complete and orthogonal. We decided to have the same construction principles for all three sorts of members. So you can have abstract fields as well as value parameters. You can pass methods (or "functions") as parameters, or you can abstract over them. You can specify types as parameters, or you can abstract over them. And what we get conceptually is that we can model one in terms of the other. At least in principle, we can express every sort of parameterization as a form of object-oriented abstraction. So in a sense you could say Scala is a more orthogonal and complete language.

He also described a difference between abstract type members and generic type parameters that can show up in practice:

But in practice, when you [use type parameterization] with many different things, it leads to an explosion of parameters, and usually, what's more, in bounds of parameters. At the 1998 ECOOP, Kim Bruce, Phil Wadler, and I had a paper where we showed that as you increase the number of things you don't know, the typical program will grow quadratically. So there are very good reasons not to do parameters, but to have these abstract members, because they don't give you this quadratic blow up.

When he gave this answer, I wasn't sure I understood exactly what he was talking about, but I think I can now offer some more insight into this difference. I have tended to use generic type parameters by default, perhaps because I come from a C++ and Java background and are more familiar with type parameterization than type members. However, I have encountered one design problem that I ended up solving with abstract type members, not generic type parameters.

The problem was that I wanted to provide traits in ScalaTest that allow users to write tests into which they can pass fixture objects. This would give people the option of writing tests in a functional style instead of the traditional imperative style of JUnit's setUp and tearDown methods. To provide this option, I needed to allow users to indicate the type of the fixture object by either specifying a concrete type parameter or member. In other words, I'd either provide this trait in the ScalaTest API:

// Type parameter version
trait FixtureSuite[F] {
  // ...
}

Or this one:

// Type member version
trait FixtureSuite {
  type F
  // ...
}

In either case, F would be the type of the fixture parameter to pass into the tests, which suite subclasses would make concrete. Here's an example of a concrete suite of tests that needs a StringBuilder passed into each test, using the type parameter approach:

// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] {
  // ...
}

And here's an example of a concrete suite of tests that needs a StringBuilder passed into each test using the abstract type member approach:

// Type member version
class MySuite extends FixtureSuite {
  type F = StringBuilder
  // ...
}

So far there's not much difference. However, one other use case I had is that I wanted to allow people to create traits that provide a concrete definition for the fixture type and could be mixed into suite classes. This would allow users to encode commonly used fixtures into helper traits that could be mixed into any of their suite classes that need them. This is where a difference showed up. Here's how you'd write that trait using the generic type parameter approach:

// Type parameter version
trait StringBuilderFixture { this: FixtureSuite[StringBuilder] =>
  // ...
}

The "this: FixtureSuite[StringBuilder]" syntax is a self type, which indicates that trait StringBuilderFixture can only be mixed into a FixtureSuite[StringBuilder]. By contrast, here's what this trait would look like when taking the type member approach:

// Type member version
trait StringBuilderFixture { this: FixtureSuite =>
  type F = StringBuilder
  // ...
}

So far there's still only cosmetic differences, but the pain point of the type member approach is about to arise. As soon as a user tries to mix the StringBuilderFixture into a suite class, you'll see a usability difference. In the type parameter approach, the user must repeat the type parameter even though it is defined in the trait:

// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] with StringBuilderFixture {
  // ...
}

But in the abstract type member approach, no such repetition is necessary:

// Type member version
class MySuite extends FixtureSuite with StringBuilderFixture {
  // ...
}

Because of this difference, I chose to model the fixture parameter type as an abstract type member, not a generic type parameter, in ScalaTest. I named the type member FixtureParam, to make it more obvious to readers of the code what the type is used for. (This feature is in ScalaTest 1.0. For the details, check out the scaladoc documentation of trait FixtureSuite.) In a future release of ScalaTest, I plan to add new traits that allow multiple fixture objects to be passed into tests. For example, if you want to pass three different fixture objects into tests, you'll be able to do so, but you'll need to specify three types, one for each parameter. Thus had I taken the type parameter approach, your suite classes could have ended up looking like this:

// Type parameter version
class MySuite extends FixtureSuite3[StringBuilder, ListBuffer, Stack] with MyHandyFixture {
  // ...
}

Whereas with the type member approach it will look like this:

// Type member version
class MySuite extends FixtureSuite3 with MyHandyFixture {
  // ...
}

One other minor difference between abstract type members and generic type parameters is that when a generic type parameter is specified, readers of the code do not see the name of the type parameter. Thus were someone to see this line of code:

// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] with StringBuilderFixture {
  // ...
}

They wouldn't know what the name of the type parameter specified as StringBuilder was without looking it up. Whereas the name of the type parameter is right there in the code in the abstract type member approach:

// Type member version
class MySuite extends FixtureSuite with StringBuilderFixture {
  type FixtureParam = StringBuilder
  // ...
}

In the latter case, readers of the code could see that StringBuilder is the "fixture parameter" type. They still would need to figure out what "fixture parameter" meant, but they could at least get the name of the type without looking in the documentation. I think there will be design situations where the explicit type member name will be better, and situations where it is worse. I think having the FixtureParam name explicit in ScalaTest FixtureSuites helps the readability of the code. But I wouldn't want to have to do that for collections. I like saying new ListBuffer[String], and am glad I don't have to say new ListBuffer { type E = String }.

My observation so far about abstract type members is that they are primarily a better choice than generic type parameters when you want to let people mix in definitions of those types via traits. You may also want to consider using them when you think the explicit mention of the type member name when it is being defined will help code readability.

What do you think of this take on abstract type members in Scala? Do you have any other insights about the abstract-type-member-versus-generic-type-parameter question to share?


Jason Zaugg

Posts: 1
Nickname: retronym
Registered: Oct, 2009

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 7, 2009 1:42 AM
Reply to this message Reply
"but the pain point of the type member approach is about to arise."

s/member/parameter/

luc duponcheel

Posts: 2
Nickname: ldatartima
Registered: Oct, 2009

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 7, 2009 2:29 AM
Reply to this message Reply
Nice (and convincing) post Bill,

I'm going to clean up some of my latest posts by switching to type members rather than type parameters

ps: I have also posted a blog some time ago that shows the elegance of type members versus type parameters

http://lucdup.blogspot.com/2008/10/scala-type-parameters-versus-type.html

Not only complexity was reduced, but, more important, also expressiveness was added

With type members I could naturally constrain children of cats to be cats, while with type parameters I could only constrain children of cats to be animals (compiler complained when constraining to cats).

ps: the code on the blog is very old, and I was just starting to learn about Scala, maybe it can be improved.

Luc Duponcheel

Rob Dickens

Posts: 15
Nickname: robcd
Registered: Feb, 2008

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 7, 2009 2:36 AM
Reply to this message Reply
Bill,

You have no choice but to use a type member if you want the type to be bounded by an inner class/trait.

They're also to be preferred if you have multiple types, where you would otherwise need to know the correct order of the type parameters.

These points I mention in section 3.2 of my paper describing a Scala API for remote monitoring and control[1].

Rob

1 http://lafros.com/macs

Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 7, 2009 5:04 PM
Reply to this message Reply
> Bill,
>
> You have no choice but to use a type member if you want
> the type to be bounded by an inner class/trait.
>
That's true and a very good point. I'll add that to my list of differences.

> They're also to be preferred if you have multiple types,
> where you would otherwise need to know the correct order
> of the type parameters.
>
Another good point. I was kind of going in that direction when talking about explicit name showing up in the code, but didn't quite get there. It is similar to the difference between regular and named value parameters in 2.8. You don't need to remember the order when you can specify "name=".

> These points I mention in section 3.2 of my paper
> describing a Scala API for remote monitoring and
> control[1].
>
Thanks for your input.

> Rob
>
> 1 http://lafros.com/macs

Dirk Detering

Posts: 16
Nickname: det
Registered: Jul, 2005

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 8, 2009 1:35 AM
Reply to this message Reply
It seems to me that the difference between parametrisation and type member may be more obvious with more parameters and more hierarchy steps.

Compare


trait Upper[S, T, U]

trait Middle[S, T, U] extends Upper[S, T, U]

trait Lower[S, U] extends Middle[S, String, U]

class MyClass[S] extends Lower[S, Int]

new MyClass[Boolean]


with


trait Upper { type S; type T; type U }

trait Middle extends Upper

trait Lower extends Middle { type T = String }

class MyClass[M] extends Lower { type S = M; type U = Int }

new MyClass[Boolean]


In the type parameter version you have to always carry the parameters around (like with method delegation).

In the abstract type member version the abstract types remain open until they are specified at one specific level (like with abstract methods in abstract classes).

And the type parameter approach comes into play on the API level: when the user of this type system instanciates MyClass.
The parameter explicitly declares the contract.

So the decision between this two ways may perhaps be a result of the consideration of what of the types is "public" from a library point of view.

luc duponcheel

Posts: 2
Nickname: ldatartima
Registered: Oct, 2009

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 8, 2009 1:53 AM
Reply to this message Reply
there are examples where type parameters are more appropriate (at least, that was my experience)

I've written a blog on DCI using function wrappers like

trait Function[X, Y] {
def fun: X => Y

def compose[Z](fyz: Function[Y, Z])
= new Function[X, Z] {
def fun = fyz.fun compose Function.this.fun
}
}

// other stuff using this trait

It was not evident for me to rewrite the blog using something like

trait Function {
type X
type Y
def fun: X => Y

// more here
}

// other stuff using this trait

especially since, sometimes, I wanted to model constraints
like Y = X (e.g. working with Function[X, X] in the type parameter case)

Luc

Eivind Barstad Waaler

Posts: 1
Nickname: eivindw
Registered: Oct, 2009

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 8, 2009 1:57 AM
Reply to this message Reply
Interesting post!

I'm writing on a master-thesis looking at Scala and DSL development. One DSL example I've been working on is a small numeric DSL for image processing. The two main datastructures here are Matrix and Image (an image is pretty much just a matrix with some extra operations). Both structures need some kind of abstract types. Matrix typically being a matrix of int or double/float, where a gray-scale-image typically will have a datatype of one int-matrix (or double-matrix) and an RGB-image typically three int-matrices..

The way I see it is that type parameters are the best option when you want the user to specify the type as some kind of external type, typically the Matrix in my example (with Int and Double):
class IntMatrix extends Matrix[Int]
class DoubleMatrix extends Matrix[Double]

Abstract types are very well suited for hidden internal types (like in your examples). I use this for my Image class:
trait Image {
   type DataType
   val data: DataType
   ...
}
 
class GrayScaleImage extends Image {
   type DataType = Matrix[Int]
   ...
}
 
class RGBImage extends Image {
   type DataType = (Matrix[Int],Matrix[Int],Matrix[Int])
   ...
}

Does this sound like a sensible separation? Or would I be better off just using type parameters or abstract types for both?

I really like the way abstract types totally hide the implementation specific details from the user of the classes.

Carson Gross

Posts: 153
Nickname: cgross
Registered: Oct, 2006

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 9, 2009 4:15 PM
Reply to this message Reply
Proof, yet again, that I am not smart enough to program in Scala.

Cheers,
Carson

Trond Olsen

Posts: 14
Nickname: tolsen
Registered: Mar, 2007

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 10, 2009 2:52 PM
Reply to this message Reply
> Abstract types are very well suited for hidden internal
> types (like in your examples). I use this for my Image
> class:
>
trait Image {
>    type DataType
>    val data: DataType
>    ...
> }
> 
> class GrayScaleImage extends Image {
>    type DataType = Matrix[Int]
>    ...
> }
> 
> class RGBImage extends Image {
>    type DataType = (Matrix[Int],Matrix[Int],Matrix[Int])
>    ...
> }

> Does this sound like a sensible separation? Or would I be
> better off just using type parameters or abstract types
> for both?
>
> I really like the way abstract types totally hide the
> implementation specific details from the user of the
> classes.

I also use something similar as you and it's centered around working with pixels (value+position) and mapping images into new ones (with a little bit of cheating by reusing objects). I'm not so concerned about hiding details. The program generates sourcecode instead of using DSL from little snippets of code. For example, inverting the colors in an image:
imageB = imageA.map(
  (in,out) => {
    out.v.r = 255 - in.v.r
    out.v.g = 255 - in.v.g
    out.v.b = 255 - in.v.b
  }
)


Another nice thing about the type system is how you can use the companion object for object construction:
object Pixel {
  type Pixel4i = Pixel[Color4i]
  type Pixel4d = Pixel[Color4d]
  type Pixel1i = Pixel[Int]
  type Pixel1d = Pixel[Double]
  def apply(r: Int, g: Int, b: Int, a: Int) = new Pixel4i(0,0,new Color4i(r,g,b,a))
  def apply(r: Double, g: Double, b: Double, a: Double) = new Pixel4d(0,0,new Color4d(r,g,b,a))
  def apply(value: Int) = new Pixel1i(0,0,value)
  def apply(value: Double) = new Pixel1d(0,0,value)
}
class Pixel[T] protected (val x: Int, val y: Int, value: T)

Raoul Duke

Posts: 127
Nickname: raoulduke
Registered: Apr, 2006

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 19, 2009 4:39 PM
Reply to this message Reply
> Proof, yet again, that I am not smart enough to program in
> Scala.

ja, as much as i liked SML more than Scheme, and i think Haskell is pretty nifty-keen, i do think you have a good point and that the typing in things like Scala is pretty off-putting. it is like there needs to be a meta-level of compiler/user interface that somehow helps generate the present-day-Scala-style code that is sort of an assembly code. or something. i mean, i still want to be able to get guarantees, just w/out so much overhead.

Jonathan Finn

Posts: 10
Nickname: lucretius
Registered: Jun, 2006

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Oct 23, 2009 12:17 PM
Reply to this message Reply
I think we really need an 'Effective Scala' book giving advice on which features to use for which problems (or patterns). You can do so much with a language like Scala that it's hard to see the wood for the trees.

The Programming in Scala book is a good start - the chapter on defining equals() is excellent IMHO. More like this would be great.

We need style advice too: there's a serious danger of each Scala programmer's style turning into his own personal DSL!

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Abstract Type Members versus Generic Type Parameters in Scala Posted: Nov 2, 2009 6:47 AM
Reply to this message Reply
Generic-type parameters are a subset of abstract-type parameters, perhaps it would be clearer if generic-type parameters were presented as a short hand for common uses. Even better if the compiler issued the same code for both, currently it doesn't.

Flat View: This topic has 12 replies on 1 page
Topic: Is Scala really more complicated than Java? Previous Topic   Next Topic Topic: Put a Flex UI On Your Application

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use