Skip to content

Instantly share code, notes, and snippets.

@pdpi
Created February 14, 2020 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pdpi/3b32cdb4b27f004828d8fe23f65ab357 to your computer and use it in GitHub Desktop.
Save pdpi/3b32cdb4b27f004828d8fe23f65ab357 to your computer and use it in GitHub Desktop.
/* Let's pretend we're building a game, which has a physics
* simulation and a renderer. We're expecting to have a
* sufficiently large collection of entities that updating in a
* functional/immutable style is too expensive, but we still want
* to avoid allowing mutation where it's not strictly required.
*
* To this effect, note how `physics` takes a list of MutableEntity,
* but `render` takes Entity instead. The physics simulation is
* expected to mutate the entities (to update their (x,y) position
* according to whatever the simulation wants to do), but rendering
* should only read the entities and use their position to draw them
* on-screen, so `render` takes a list of Entity instead.
*/
fun physics(entities: List<MutableEntity>) { /* Do the physics */ }
fun render(entities: List<Entity>) { /* Do the renders */ }
/* To achieve this goal, we start with an immutable Entity, then extend
* it into a mutable MutableEntity. Because of the subtype relationship,
* MutableEntity is allowed as a drop-in replacement for an Entity
*/
open class Entity(
open val x: Int,
open val y: Int,
val picture: ByteArray?
)
class MutableEntity(
override var x: Int,
override var y: Int,
picture: ByteArray?
) : Entity(x, y, picture)
/* We could achieve the same effect with an interface/class pair,
* but we're still fundamentally implementing the vals with vars,
* which is the supposedly icky behaviour we "should" be avoiding.
*/
interface AlternativeEntity {
val x: Int
val y: Int
val picture: ByteArray?
}
class AlternativeMutableEntity(
override var x: Int,
override var y: Int,
override val picture: ByteArray?
) : AlternativeEntity
fun main(args: Array<String>) {
/* "Immutability" is a very overloaded term. Here, `ents` is
* an immutable handle to an immutable list of mutable elements.
*/
val ents = listOf(
MutableEntity(0, 0, null)
)
while (true) {
/* At the call site, we're using the same list of mutable
* elements, but the functions' types enforce the contract
* that physics can mutate the entities but render can't.
*/
physics(ents)
render(ents)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment