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

Loïc Hoguin essen@REDACTED
Fri Jan 25 03:08:36 CET 2013


On 01/25/2013 01:31 AM, Richard O'Keefe wrote:
>
> 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.

It's about ease of use, reducing the extra steps (or plane flight in the 
case of Erlang) to get the work done.

>> 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.

You should play more games. Some recent games (actually probably games 
from these past 20 years or more, though it's been quite refined) 
feature weapons that can be upgraded. They also feature weapons that 
have abilities. Not the character, the weapon itself. For example a 
shotgun could have a spread damage ability that you can unlock if you 
spend enough points. You can probably guess that giving another 
character this weapon would be pretty lame if the weapon's upgrades 
didn't stick. Especially in the context of online games where selling 
items is fairly common.

>> Or at least, not without a lot more keystrokes.
>
> If you're worried about keystrokes, it's DSL time.

Sure. Then Erlang needs that DSL.

>> 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

Let me stop you right there. Nobody said anything could change 
Character. In Erlang it would quite naturally belong to a gen_server and 
you'd make calls like {add_item, Item} and let the gen_server handle it. 
You'd of course program the gen_server to handle any needed logic (like 
inventory full).

The problem is that in Erlang, if I were to use records for example, I 
would have to take the inventory from the #character record, add the 
item in it, and then put it back in the #character record. This sounds 
bearable, you'll probably say. Now imagine if the character could have 
one or more sidekick avatars (like, say, a dog and a cat), and you want 
to add the item to your first sidekick's inventory. This is PHP:

   Character->avatars[0]->inventory[] = Item;

In Erlang, I'd have to update two records and two lists, which would 
take quite many lines. Erlang should allow us to focus on the game 
logic, not on trying to avoid mistakes while updating data structures.

> 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.

Irrelevant as explained above. This doesn't prevent transactional 
behavior, you just have to wrap it in a gen_server *like everything else*.

>> 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.

To reuse your terms, right now, in Erlang, you don't have low level. You 
have basement level. You have to change every single element in a 
hierarchy one by one and then build it back together. OR generate code 
to do that (and hope that code doesn't have bugs). OR write all that 
logic in Lua and reduce the size of the code and the potential number of 
bugs in it by 3.

> 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.

Of course it was there. He had to define the combinator.

[snip more irrelevant demeter stuff]

>> 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.

Of course I want less low-level code in my program. That's why I want to 
be able to update data structures in one line instead of five.

> 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.

Sure. And who has to write the code for addToInventory? Someone does. 
It'd typically check you have space left in your inventory, and if the 
item is stackable and you already have a stack of them in it, increment 
by 1 the stack, otherwise add the item. And to add the item I need to 
update one and more record and one and more list, which takes quite a 
few lines.

Who writes this? Because that's the part that's problematic in Erlang. 
Not putting things behind a gen_server or a module or a DSL.

> 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.

I think you should go take a nap.

-- 
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu



More information about the erlang-questions mailing list