To me, these are "Tell bar's foo to do something with baz." and "Tell foo to do something with bar and baz.". So being 'able' to flipflop the syntax is at least temporarily semantic'ly confusing.
To me, these are "Tell bar's foo to do something with baz." and "Tell foo to do something with bar and baz.". So being 'able' to flipflop the syntax is at least temporarily semantic'ly confusing.
Why is bar working with baz and not baz working with bar?
I struggle with this in Unity:
Player.collect(PickUp) // this?
PickUp.boost(Player) // ...or this?
Instead, the code should describe the interaction between the two units of state: onCollide(Player, Pickup)
If we structure our code like in the last example, it makes sense to weaken the `a.b` vs `b(a)` distinction, and instead use the dot as a kind of pipe-operator.So, in your case, depending on other modeling decisions, I could argue either for
onCollide(Player, Pickup) - if Player and Pickup are both plain data, and don't need to guarantee any invariants
Player.collect(Pickup) - if Player actually has to ensure some invariants such as health<100)
I don't see any good arguments for pickup.boost(Player), in typical games. Of course, if both the Pickup and the Player have some invariants that need to be maintained on a collision, then arguably the design has to be changed at a deeper level.
Although I rarely see Objects used this way. Often, methods are used to implement all related functionality. Unity even strongly encourages this. (...at the moment. They are working on Entity Component Systems which will work more similar to my third example)
I concede that languages shouldn't use the dot as a syntactic tool, be it through Extensions[1] or UFCS, but rather offer a pipe-operator. If they don't, I'd still prefer UFCS rather than no way of chaining at all.
[1] Extensions for interface/protocol conformance are fine of course.
What is the code driving it?
- looping over players to update them?
- looping over objects to update them?
- some other event loop?
I actually like the third least:
The first two tell me what is happening, but the third doesn’t — I could be colliding to block motion or I could be picking up.
In other words, to me it's simpler and therefore less confusing.
foo.do_this(bar).do_that(baz)
instead of do_that(do_this(foo, bar), baz) do_this(foo, bar) |> do_that(baz)
Or an explicit version: do_this(foo, bar) |<> do_that(<>, baz)
(Yes, this is a bastardization of Elixir's pipe and some Lisps' arrow macros.)E.g. in D to convert between different types you can use std.conv:
import std.conv;
"123".to!int;
123.to!string;
Ints don't need to understand string building and strings don't need to understand int parsing. The conversion code just needs to declare a couple functions taking the right arguments and it just works. To me the above is much more readable than to!int("123")
to!string(123)
in any case. It's also quite nice when dealing with C APIs since it allows you to pretend they are OOP in quite a lot of cases. e.g. with SDL: SDL_CreateRenderer(window, -1, 0);
turns into window.SDL_CreateRenderer(-1, 0);
and say I'd like to have a function to initialize all the renderer stuff in one go? I can simply declare void CreateRendererAndInitialize(SDL_Window* window){<snip>}
and now you can do the following: window.CreateRendererAndInitialize();
It removes a lot of pain from extending 3rd party types you see in other languages.