Exploring Data, Context and Interaction through prototypes with Io

Data, Context and Interaction is a programming concept that was once picking up steam in the Ruby world. The general idea is to separate data objects from any code specific to the context in which the data will be used. Objects within a given context interact through “roles”, which are stateless representations of a set of responsibilities. True DCI, for the dogmatically inclined, has further caveats. The Wikipedia article on DCI gives a decent enough summary.

It has since been found that the most common way of implementing DCI in Ruby, through mixing a module into the metaclass of an object, has terrible performance implications. An alternative is to use delegation, although in my experience delegation can lead to confusion over object identity.

The 1991 paper Organizing Programs Without Classes (PDF) makes a compelling case for the power of prototypal inheritance. It is contended that a combination of trait objects and prototype objects allows for code reuse and object composition in a more flexible way than class inheritance.

One of the proposed benefits of traits and prototypes is the ability to change the “behaviour mode” of any data type instance at runtime through dynamic inheritance. Figure 5 in the paper demonstrates quite neatly the authors’ investigation into dynamic inheritance with Self.

Which brings us back to the idea of roles. Roles in the DCI sense are easily represented as traits objects; containers for related functionality that is injected into a data object in a specific context. A prototypal language where dynamic inheritance is encouraged and comes with no performance penalty handles this job well.

Io offers a flavour of prototypal programming inspired by Self and others. Here’s a run-through of a common DCI example — the bank transfer — using Io.

An Account object, cloned from the basic Object, forms the basis of a bank account. Any clones will be initialised with an arbitrary balance.

Account := Object clone do(
  init := method(self balance := 100)

Here’s a traits object that handles deposits.

DepositingAccount := Object clone do(
  deposit := method(amount, balance = balance + amount)

A data object is cloned from Account and the traits object DepositingAccount is injected into it.

account ::= Account clone
account appendProto(DepositingAccount)

Inheritance is most often achieved through cloning objects but through direct manipulation of the prototype list, objects can inherit from multiple others.

account protos
  #==> list(Account_0x7fae4acaa610, DepositingAccount_0x7fae4aca0af0)

The data object can carry out its role…

account deposit(10)
account balance
  #==> 110

…and the depositing capability can be removed afterwards.

account removeProto(DepositingAccount)

With a traits object that adds withdrawal behaviour, it’s easy to write the context for a simplified bank transfer. In this case it’s just a transfer method in place of whatever controller or service would assemble the objects in the real world.

WithdrawingAccount := Object clone do(
  canWithdraw := method(amount, balance >= amount)
  withdraw := method(amount, balance = balance - amount)
transfer := method(amount, from, to,
  from appendProto(WithdrawingAccount)
  to appendProto(DepositingAccount)

  if(from canWithdraw(amount)) then(
    from withdraw(amount)
    to deposit(amount)
  ) else(
    message := "Cannot transfer #{amount}, balance is #{from balance}"
    Exception raise(message interpolate)

  from removeProto(WithdrawingAccount)
  to removeProto(DepositingAccount)

  list(from, to)

Of course it’s easy to tout the benefits of a hobbyist language and we need to work with the languages we have in production, but ever since investigating this subject I find myself wishing that the languages I work with every day could offer dynamic multiple inheritance like Io.

Would we still be fastidiously moving presentational methods into decorator objects if Ruby had usable instance composition? Would we still have hundreds of attempts to wedge classes into JavaScript if its own prototype system was more flexible?

The code from this post (with a bit of extra namespacing) is available on GitHub. Credit to Fogus for recommending Organizing Programs Without Classes.