Fork me on GitHub

Hacking Scala

#scala #hacking

April 28, 2013 at 2:01am

Composing Your Types on Fly

Let me show you class from one of my projects:

class FileTransferHandler extends TransferHandler with Publisher {
    // ...
}

TransferHandler is swing class that generally handles copy/paste and D&D stuff. Publisher is trait that comes from scala-swing. So I generally composing here 2 independent things and making then work together.

As next step I define method, that requires it’s argument to be both - TransferHandler and Publisher. In my codebase I have only one type that extends both of them, and it’s FileTransferHandler. So I can use it to define my method, right?

def register(handler: FileTransferHandler) {
    // ...
}

The problem here is that the body of the register method does not care whether handler is FileTransferHandler or ImageTransferHandler or even StringTransferHandler. It only requires it to be TransferHandler and Publisher. And it’s exactly what Scala allows you to express using with keyword! So now we can write improved version of register method:

def register(handler: TransferHandler with Publisher) {
    // ...
}

So you don’t actually need any concrete class or trait to express such constraint.

Combining with structural types

Scala has very nice feature called structural types. Let me describe it to you in example. Recently I was writing some Scala tests for Java classes. It appears, that a lot of classes had methods like init() and destroy(). Not all classes had both of them - some need only to be initialized, but don’t require explicit destruction and vice versa. Also there are no interfaces for this - they all were normally instantiated using spring with something like

<bean class="..." init-method="init" destroy-method="destroy" />

Structural typing helped me a lot to deal with such classes. I was able to define methods like this one:

def cleanup(list: List[{def destroy()}]) = 
    list foreach (_.destroy())

Next I would like to define method, that will initialize and destroy objects automatically for me, because I can just forget about one of these steps. So here my first attempt:

def safe[T <: {def init(); def destroy()}, R](t: T)(body: T => R) =     
    try {
        t.init()
        body(t)
    } finally {
        t.destroy()
    }

safe(new PaymentService) { service =>
    service.makePayment()
}

So it works! But can we implement it better? The problem here - is that we repeating ourselves. I already used signature of destroy method in the cleanup method, and I don’t want to repeat myself again. It would be nice to define some traits/interfaces like Init or Destroy, but I don’t want to change all existing classes in order to implement them. Actually I even don’t need any concrete trait for this - I just need to define some kind of alias for {def init()} and {def destroy()}.

Type aliases serve exactly this purpose. Let’s define them:

type Init = {def init()}
type Destroy = {def destroy()}

Nice, now I have pretty names for the structural types. safe method needs both of them and, as you probably already guessed, I can combine them using with:

def safe[T <: Init with Destroy, R](t: T)(body: T => R) = // ...

If you wish, you can also define type alias for the combination:

type InitDestroy = Init with Destroy

def safe[T <: InitDestroy, R](t: T)(body: T => R) = // ...

Hope you enjoyed.

Notes

  1. bewrabawa reblogged this from hacking-scala
  2. hacking-scala posted this