[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