resinten / dathos-game-engine

WIP game engine written in Rust for work with Rust & Ruby
6 stars 0 forks source link

Consistent usage of `!` in method names #5

Open resinten opened 4 years ago

resinten commented 4 years ago

In Ruby, methods that involve mutation of values (those with side effects) commonly end with !. This is a really nice way to look at a series of methods and know whether the method might modify state in some way. But in a game, nearly all methods are likely to involve mutation of state in some way:

Draw.load_spritesheet!
Draw.create_sprite!
Draw.text!
update!
run!

It seems like it loses its meaning if everything has !, so I would like to form some best practices for how the library will use this suffix. Two options come to mind:

  1. accept the ubiquity of ! and use it with its normal meaning
  2. treat ! as indicating an effect that persists over multiple frames (the frame serves as the mutation boundary)

In the second option, Draw.load_spritesheet!, Draw.create_sprite! and run! would retain the ! indicator, as the spritesheet loading affects a list of globally available spritesheets, and run! initiates a coroutine of code that will run over many frames. Draw.text and update (maybe not update?) would lose the ! as they have a natural frame mutation boundary. Possible exception here is that update! could last over multiple frames since it might call run!.

maalur commented 4 years ago

In Ruby, methods that involve mutation of values (those with side effects) commonly end with !. This is a really nice way to look at a series of methods and know whether the method might modify state in some way.

I think this is a really good description of bang methods: http://rubylearning.com/satishtalim/writing_own_ruby_methods.html

The way I interpret it is that the method is "dangerous" in some way and that one should probably avoid using this method unless they have a specific reason to. "Dangerous" is totally subjective and dependent on the context the method is being defined in.

The Ruby standard library typically uses bang methods that modify the object they are acting on instead of returning a new object, but other libraries use it for other reasons. For example, in the ActiveRecord library, which is the standard Rails library for interacting with a database, bang methods are used to raise errors when an attempt to modify the database fails validations.

But in a game, nearly all methods are likely to involve mutation of state in some way: ... It seems like it loses its meaning if everything has !, so I would like to form some best practices for how the library will use this suffix.

You make a great point. If everything is special then nothing is really special, in which case I would personally drop all of the bangs. If changing state is expected behavior and is something that should be done regularly, then there isn't anything dangerous or unexpected happening.

Since this is your library, you get to decide what "dangerous" means and when to define bang methods. That could be when a method has effects that persist over multiple frames (if that is unexpected behavior) or something else entirely.

In general, I like to limit bang methods to a couple different use cases:

  1. "Dangerous" versions of existing non-bang methods, the standard use case
  2. Methods that do something unexpected, to warn developers that they need to carefully consider if this is the method they really want to use.
  3. Methods that should probably be private and are part of some lower level API that I've had to expose publicly for some reason, to indicate that it probably shouldn't be used unless you have a very specific reason to.

I hope this helps!

resinten commented 4 years ago

Yes! This is very helpful. I've always looked at it as indicating "side-effect-y" behavior, since I'm coming from a Rust and Scala background, but "dangerous" seems like another good way to look at this. For instance, an exception to "side effects" is that the array .push method mutates the underlying array (a side effect) but doesn't have an exclamation mark. My current thinking then is that methods that perform output or last multiple frames would be considered dangerous because they have potential ramifications that extend outside the current frame.