More OO techniques in Elm

Composition versus Inheritance - Mixins.

Here is a Gist demonstrating the use of Mixins or Traits in Elm:

https://gist.github.com/rupertlssmith/fe24d4f740fa9b622a3c329fc2330772

Often in OO programming we are told, “prefer composition over inheritance”. Large trees of classes with deep inheritance are successful ways of describing component based UI tool-kits, but rarely so successful in other domains.

Here is a set of game objects defined as an OO hierarchy (http://machinethink.net/blog/mixins-and-traits-in-swift-2.0/):

hierarchy

The issue in this example is that the Zap Monster and the Castle can both fire lasers guns, but no other objects in the game can. Once we have writen the laser gun code for a Zap Monster, how do we go about writing the same code for a Castle? We could copy-paste the code, which is almost always a bad idea. We could put the code on a base class further up the hierachy, on the GameObject class, but that means the code is available on other game objects that should not have it. A better way might be to put this code in a seperate class, ShootingHelper (or FireTrait in my Elm code), and to have an instance of that inside the Castle and Zap Monster classes to delegate to. This way of re-using code is called a mixin or trait.

It is the purpose of my Gist to show how this can be done in Elm. I only defined one trait in the Elm code, but you can see that is an extensible record, so traits can be combined.

1 Like

Hmmm, this made me think! :+1: :thinking:

If I understand correctly, this is a fancy version of passing a function to a function?

For example, this function using traits

updateForFiring : FireTrait s a -> s -> GameState -> GameState
updateForFiring fireTrait gameObject state =
    fireTrait.fire gameObject state

could be written by passing a function instead as:

updateForFiring : (s -> GameState -> GameState) -> s -> GameState -> GameState
updateForFiring fireFunction gameObject state =
    fireFunction gameObject state

If you have to pass a lot of functions (say more than 3) I could see wanting to bundle them together into a record. Are those the kinds of situations when you might use something like this?

1 Like

Yes, it is. And if you only have one function to pass, there is no point really in putting it in a record. Except in this case the record is extensible, so you could still do that if you are planning on combing lots of small traits together. Perhaps I need a better example.

One that I have in mind at the moment is this:

type alias EqualityTrait s a =
    { a
        | equal : s -> s -> Bool
        , hashCode : s -> Int
    }

This could be used to define custom equality comparison on arbitrary records, and then data structures such as hash tables, sets and so on implemented around it. Exactly how java.util.* works, which is where I take this idea from.

To give an example of where customizing equality could be useful - suppose I load a list of items from a back-end and each item already has a unique id on it. I could compare the records just by their ids. That way I can put the records in a set. If I change one, inserting it into the set will overwrite the previous value. The set could be made to behave differently with a different implementation of equality that compares the records by the value of all of their fields.

I was guided on Slack to look at this:

http://package.elm-lang.org/packages/seurimas/slime/latest

Which is an entity-component-system for games in Elm. ECS is also a composition-over-inheritance technique:

Looks very interesting and I am also intrigued that the Elm game dev community are exploring stuff that is a bit more “out there” (in a good way).

I am developing a diagramming tool. The elements in my diagrams have a lot of common behaviour, but also some behaviour unique to the type of diagram element. I also need to be able to add (spawn) elements, remove them, and have time-based behaviours like zoom animations and so on. This is the reason I am looking into ways of re-using common behaviour as well as coding in an open-ended way, where new diagram elements can easily be added. So the above could be a good fit for my application too.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.