[erlang-questions] If you are homesick for object.selector

Richard O'Keefe ok@REDACTED
Fri Jan 25 01:31:25 CET 2013


On 24/01/2013, at 3:37 PM, Loïc Hoguin wrote:
> But I think their point is that Person.name does not require extra keystrokes to get the value, as opposed to Person#person.name or person:name(Person).

I thought the complaint was that Person#person.name is not polymorphic;
my point was that name(Person) _can_ be polymorphic, if you seriously
want it to be.
> 
> They're incorrect when claiming it makes things harder, it's just a lot more tedious, especially if you have a lot of different values to manipulate. A simple example would be an RPG video game character, which has various kinds of state, a name, a class, stats, equipped items, inventory, unlocks, friends, quickchat, options and more. Erlang makes programming this kind of thing a nightmare, and all that simply because you can't do something like:
> 
>  Character.current_weapon.ability.points_req
> 
> Or
> 
>  Character.inventory[0].name

Don't OO programming courses teach "The Law Of Demeter"
(http://en.wikipedia.org/wiki/Law_of_Demeter) any more?
(It's also known as 'don't talk to strangers': send
messages to yourself, your instance variables, and the
parameters and local variables of the current method.)

This particular example is actually a good illustration
of what can go wrong:  "ability" is not a property,
it is a property relating a character to a category of
weapons.  If one character drops a weapon and another
picks it up, the second player should not acquire the
first player's ability.  So it should be something
like
	(player abilityWith: (player currentWeaponCategory))
and then points are required to do something, so it _really_
should be something like
	(player pointsRequiredTo: #fightOrcs
                using: player currentWeaponCategory)
and note that it's
        player currentWeaponCategory
not     player currentWeapon category
because the player might not _have_ a currentWeapon.

> 
> Or at least, not without a lot more keystrokes.

If you're worried about keystrokes, it's DSL time.

> And let's not even speak about actually modifying these values or adding an item to the inventory. In a language as horrible as PHP, adding an item to the inventory can be written as:
> 
>  Character->inventory[] = Item;

That is *precisely* one of the things that makes it a truly horrible
language.  "Don't let strangers touch your private parts."  An object
such as a character *must* remain in charge of what happens to its
components (such as an inventory) because otherwise you are not doing
OO programming, you are just hacking around in the dark.  The OO way
to add an item to a character's inventory is to *ask the character to
do it*, so the right way to do that is

	character addToInventory: item

If you let things like Character->inventory[] = Item happen, you
_also_ let random bits of code reorder your inventory, delete
things from your inventory, and add things from your inventory
to another player's inventory without withdrawing them from
yours.

If Erlang turns people away because it doesn't let people write code
like that, then it's turning away people I am desperately keen to see
NOT touching any code that can affect my life.

Just now there is a seriously broken piece of software affecting
many lives in New Zealand.  It's called Novopay (Google it!) and
it is the payroll system for all school-teachers and support staff
in this country.  It's overpaying some people, underpaying more,
and not paying others at all.  The last time I looked at their
admitted defects web site, one of their forms had refused to accept
the entry of apostrophes in people's names.  Well, nobody expects a
large system to work correctly first time; that's why you roll it
out gradually (except the government didn't choose to do that) and
why you keep the old system on hot standby so you can roll back to
something that (now, after long effort) works quickly when it
turns out that people aren't getting paid (except the Government
chose not to do that either and say it is now impossible to go back).
I hereby make two retrodictions:
  (a) The designers did not try to follow the Law of Demeter.
      (I will give NZ$20 to a local school if I am wrong.)
  (b) The critical aspects of the code (dealing with a lot of
      different kinds of contracts) were not developed using an
      embedded domain specific language.
      (Again I will give NZ$20 to a local school if I am wrong.)
   
> You of course have to know what [] means, but it makes the code incredibly easier to read.

I'm not at all sure that I believe it at all.

What you are talking about is ease of writing *low level* code.
Not "small scale".  "Low level".  The kind of stuff you DON'T want
to make easy to read, but next to invisible.  In fact, the kind of
stuff you don't want to write at all, because if you didn't write
it, you didn't wrong it.

One of several enlightening experiences I've had was being in the same
room with a real Haskell expert who was finding some code starting
to get messy.  He thought for a couple of minutes, and said

	"I'll just define a combinator for that."

Shortly afterwards, his code was markedly smaller and a lot closer
to doing what he wanted.  He wasn't making the low level data
structure manipulation easier to read, he was making it NOT THERE
in the source code.

I've read Apple's Key-Value Coding manual, and as I remarked earlier
this week, I've implemented something close to it in my Smalltalk
system.  However, there's one "feature" I chose _not_ to imitate:
if you try to use a selector, and there is no method by that name,
but there _is_ an instance variable, KVC will give you direct
access to that variable.  You can switch that off, on a per-class
basis, but it's on by default.  What this means is that _unless_
you have been careful to switch that feature off in every class
you have, using KVC voids *every* guarantee your classes want to
offer.

Let's face it, trying to follow the Law of Demeter is hard.
It means taking encapsulation and system integrity _seriously_.
It means that when something is awkward to say, you DON'T just
smash-and-grab, you redesign.  It means that you try to provide
conceptual-level interfaces.

> Erlang lacks all these easy data manipulation facilities. I am not sure this can be fully resolved.
> 
> Of course, perhaps one could use some Lua behind Erlang to do the game logic, but that's not really a good selling point for people to use Erlang, it's at best a compromise.

That's completely back to front.

You don't want *more* low-level code in your program,
you want *less*.  You devise a DSL in which the things
you need to say about the game logic are easy to say and
many of the mistakes you want to avoid cannot be
expressed and where the data structure manipulation is
just plain NOT THERE in the source code.  It's in the
translation of the source code.

And it doesn't matter whether the low level code is going
to be in Erlang or Ada or Lua or F#.  You *still* want to
write your game logic at the highest level of abstraction
you are comfortable with hiding as much data structure
manipulation as you can.

Now, a DSL can be an embedded DSL, in which you have the
facilities of the host language as well as the DSL ones.
The DSL might even be a module or two just providing
some handy functions.  Basically, that's the Smalltalk/
Law of Demeter approach.
	character addToInventory: item
is domain-specific code.  It talks about characters, and
items, and inventories, and says NOTHING about the data
structures representing them.  The inventory could be an
SQLite table.  It could be a concurrent process.  It
could be a hash table.  And this is where it gets really
nice: if you turn your game into a MPORPG where game
state is replicated and changes have to be broadcast,
your code that works at this level does not need to be
changed.

However, while the EDSL approach makes it _possible_ for
your game logic to be written at a high level, it need
not _prevent_ low level details (alias future bugs)
creeping into that logic.  And the checks the compiler
can do are still almost exclusively the checks the
compiler knows how to do for the host language.

A free-standing DSL lets you provide *concept-level*
consistency checks, which is a thing well worth having.


Sorry about the rant, but for reasons I'll spare you I
didn't get any sleep last night and I am just so _tired_
of people wanting to write code they should be running
away from.




More information about the erlang-questions mailing list