Functional programming and Text adventures

functional programming

This is mostly a theoretical question about FP, but I'll take text adventures (like old-school Zork) to illustrate my point. I'd like to know your opinions on how would you model a stateful simulation with FP.

Text adventures really seem to call for OOP. For example, all the "rooms" are instances of a Room class, you can have a basic Item class and interfaces like Item<Pickable> for things you can carry and so on.

World modeling in FP works differently, especially if you want to enforce immutability in a world that must mutate as the game progresses (objects are moved, enemies are defeated, the scoring grows, the player changes its location). I imagine a single big object World that has it all: what are the rooms you can explore, how they are linked, what the player is carrying, what levers have been triggered.

I think that a pure approach would be to basically pass this big object to any function and have it returned by them (possibly modified). For example, i have a moveToRoom function that gets World and returns it with World.player.location changed to the new room, World.rooms[new_room].visited = True and so on.

Even if this is the more "correct" way, it seems to be enforcing pureness for the sake of it. Depending on the programming language, passing this potentially very big World object back and forth may be expensive. Also, every function may need to have access to any World object. For example, a room may be accessible or not depending on a lever triggered in another room because it may be flooded, but if the player carries a life jacket, it can enter it anyway. A monster may be aggressive or not depending on whether the player has slayered his cousin in another room . This means that the roomCanBeEntered function needs to access World.player.invetory and World.rooms, describeMonster needs to access World.monsters and so on (basically, you must pass the whole load around). This really seems to me to call for a global variable, even if this is all but good programming style especially in FP.

How would you solve this problem?

Best Answer

Mind that functional languages use data structures and separated functions instead of objects. For example you would have a set of rooms and a list of inventory items as a world instead.

Ideally you would also limit the amount of data you give to functions to how much they actually require as much as possible instead of passing the entire world (say you extract a single relevant room from your world; of course completely interdependent worlds can be hard to separate). The result would be reincorporated into the world data structure, creating a new state. You cannot model state without using state; as you say some things inherently require mutation.

Most practical functional languages provide a way to realize mutation either directly (say the ST monad in Haskell or transients in Clojure), or simulate it efficiently (often by reusing unchanged parts of the data structure (Clojure's default data structures are a good example here)). Either way purity is maintained.

Since the amount of state that needs to be mutated seems limited, I wouldn't worry too much about performance issues and go with the (possibly already optimized) naive functional approach.

A different option I have seen would be returning only instructions to change some part of the world from your individual functions, and then updating your world according to these. A series of blog posts describing this is available at http://prog21.dadgum.com/23.html.

Both of these answers deal more with how to organize changes than to not pass your entire world to functions, because a perfectly interdependent one cannot be segmented by definition - but try and do so as best as you can in your case, which is not only functional, but also good practice.