Created
February 14, 2020 22:18
-
-
Save pdpi/3b32cdb4b27f004828d8fe23f65ab357 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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