BETA - An Example
I've kept meaning to post an example of how the BETA programming language handles inheritance and now I have a touch of time to do so. It may seem very strange to you, but one you see what's going on, it actually makes sense.
Consider an employee class. You might have name, department, and totalHours attributes. In BETA, you don't have classes, you have patterns. A pattern is more than a class, but pretend they're the same. Here's what the employee pattern might look like in BETA:
employee: (# name: @text; dept: ^Department; totalHours: @integer; registerWork: (# noOfHours: @integer enter noOfHours do noOfHours + totalhours -> totalHours #); computeSalary:< (# salary: @integer do inner exit salary #); #);
Even without really explaining any of that, you can figure out what it means (BETA seems to have decided to take the horrifyingly ugly syntax route and add extra sigils to make it worse, but clearly Perl has cornered the market here). You probably see things like registerWork and computeSalary and think "methods" and you wouldn't be far off, though computeSalary doesn't seem to have a body which makes sense.
Let's consider that the employee class is abstract and what we really have are workers and salesmen. Here's what their classes might look like:
worker: employee (# seniority: @integer; computeSalary::< (# do totalHours * 80 + seniority * 4 -> salary; 0 -> totalHours #) #); salesman: employee (# numSoldUnits: @integer; computeSalary::< (# do totalHours * 80 + numSoldUnits * 6 -> salary; 0->numSoldUnits->totalHours #) #)
As you can see, their computeSalary methods are fleshed out, so these are obviously the concrete implementations of the abstract method.
But they're not.
When you create an employee object, you'll eventually call their computeSalary method, perhaps for adding it to your payroll total:
When you call the computeSalary method on the worker object, you do not get the worker.computeSalary method! Instead, you get the employee.computeSalary method. Why? Because it might actually be defined like this:
computeSalary:< (# salary: @integer do (* optionally do something here *) inner (* this calls the worker.computeSalary *) (# do (if salary < 0 then 0 -> salary (* an exception is better *) if)#) exit salary #);
What really happens when you use objects in BETA is that the method resolution order is reversed and the order starts at the top of the tree and the objects (patterns) call their subpattern methods for you. When the result is returned, the object can decide if the result is acceptable and take appropriate action. In other words:
- Almost automatic enforcement of Liskov substitution principle
- You can't accidentally override superclass methods or forget to call them
If you think about this, it makes sense, but it's a strange way of thinking about inheritance. The primary class definition is truly responsible for ensuring its contract is honoured, but I've never seen this any where else.
And if you're wondering, BETA does not support multiple inheritance. Instead, they have what they call "part objects" to handle this for them. Oddly, it's not that they're against MI, but they've never found a clean way to incorporate it, despite the language project starting in 1976.
To learn more, you can also download a free PDF of the BETA book.