Mark Needham

Thoughts on Software Development

Scala: Self type annotations and structured types

with 5 comments

A few days ago I tweeted that I didn’t really see the point in structured types in Scala

Not sure I understand where you would use structural types in #scala instead of defining a method on a trait http://bit.ly/jgiW7b

…but today my colleague Uday came up with a cool way of combining self type annotations with structured types inside a trait we defined.

We had some code duplicated across two classes which looked roughly like this:

class OnePageType {
  lazy val peopleNodes = root \\ "SomeNode" \ "SomeSubNode" \ "People" \ "Person"
  private def fullName(personName: Node): String = // code to build person's name
 
  lazy val people: String = peopleNodes.map(fullName).mkString(", ")
}
class AnotherPageType {
  lazy val peopleNodes = root \\ "OtherNode" \ "OtherSubNode" \ "People" \ "Person"
  private def fullName(personName: Node): String = // code to build person's name
 
  lazy val people: String = peopleNodes.map(fullName).mkString(", ")
}

The first line is different but the other two are identical because the data is stored in exactly the same format once we get down to that level.

Since We want to keep the XPathish queries as descriptive as possible so that we don’t accidentally end up pulling the wrong elements onto the page, making those a bit looser wasn’t an option in this case.

Instead we pulled out a trait like so:

1
2
3
4
5
6
7
trait People {
  self: {val peopleNodes: NodeSeq} =>
 
  private def fullName(personName: Node): String = // code to build person's name
 
  lazy val people: String = peopleNodes.map(fullName).mkString(", ")
}

Which we include in the classes like this:

class OnePageType extends People {}
class AnotherPageType extends People {}

What we’re done on line 2 of the People trait is to define a self annotation which says that we need a val of peopleNodes to be present on the classes in which the trait is mixed.

If a val of peopleNodes doesn’t exist then the class won’t compile!

In this case the structure type works quite well because we wouldn’t really want to pull out peopleNodes into a trait just to reference it as a self type annotation.

Be Sociable, Share!

Written by Mark Needham

June 27th, 2011 at 11:21 pm

Posted in Scala

Tagged with

  • Steve McJones

    This is brilliant!

  • http://twitter.com/przemekpokrywka Przemysław Pokrywka

    “we wouldn’t really want to pull out peopleNodes into a trait just to reference it as a self type annotation”

    What’s the reason you prefer self-type annotation here? Having peopleNodes as an abstract member of People trait would work in the same way here. And it’d be simpler.
    Or is it just to have some cool, fancy stuff like self-type annotations in code? ;)

  • http://www.markhneedham.com/blog Mark Needham

    @twitter-137622400:disqus  we actually didn’t think of that – I guess when you’re looking at ways to get a certain approach into your code then your vision gets narrowed into only seeing that option!

    I dunno that I agree about it being simpler – perhaps it would make more sense to someone who’s more used to the Java/C# style of structuring code but visually I don’t see much difference between the approaches.

  • http://twitter.com/przemekpokrywka Przemysław Pokrywka

    Abstract member is simpler in the sense, that it doesn’t involve two specific features of Scala (structural types and self-type annotations), besides of that the abstract member concept is pretty common in other languages and even GoF uses it in the Template Method pattern. So yes, it does make more sense for people used to Java/C#, but it’s only an advantage IMO.

    I’d rather use both presented features in places, where ordinary familiar Java/C#-style approach is suboptimal.
    Structural typing is great for classes that have some methods with the same method signatures, yet without common ancestor (and we cannot modify them). Handling of close() method in some java.io classes is a good example, what structural types can be useful for.
    Self-type annotation is technically equivalent to declaring a bunch of abstract members. If I were to declare six template methods in my class, three of which related to some single concern I would definitely extract those three to a separate trait and then declared self-type to that trait’s type.

    Your example is neither particularly well-suited for structural types, nor for self-type annotation. You neither have classes outside of your control, neither have you multiple related abstract members (you have only one).
    So while you’ve demonstrated a new way of using both of the features together, I wouldn’t really recommend that style in any (non proof-of-concept) Scala project. Actually it adds some tiny bit of complexity without any real benefit. IMO with maintainability in mind you should resist temptation to use advanced (even exciting) features of a language, when a simpler (and actually more concise and readable) solution exists.

    Of course I’m also very excited with Scala features, but using them suboptimally can make some people get an impression, that “Scala is too complex” for them.

  • http://twitter.com/przemekpokrywka Przemysław Pokrywka

    Just blogged on the topic of cases, when you can combine self-type annotations with structural types and it still makes sense: http://goo.gl/KS4Lv