Accidentally Introducing Side Effects into Purely Functional Code
Programming in the 21st Century - James Hague - December 14, 2008It’s easy to taint even purely functional languages by reintroducing side-effects. Simply have each function take an additional parameter representing the global state of the world—a tree of key/value pairs, for example—and have each function return a new state of the world. This is not news. It’s an intentionally pathological case, not something I’d ever consider implementing.
What’s more surprising is how easy it is to accidentally introduce side-effects.
For the Purely Functional Retrogames series, I wrote code that operated on a list of game entities:
[A, B, C, D,...]
Each element was a self-contained unit of sorts: an ID, x/y position, current state. Using this list of entities to build a new version for the next game frame was a simple map operation. The ID and state for each entity were used to call the correct transformation function for that entity.
Each of these transformations had three possible outcomes: a new entity would be returned with a different position and/or state, an entity could delete itself, or an entity could create some new entities (think of dropping a bomb or firing a shot). All three of these can be handled by having each transformation function return a list.
For example, if the original list was:
[A, B, C, D]
and entity “B” deleted itself, and entity “C” created four new entities in addition to a new version of itself, then the returned values might look like this:
A => [A1] B => [] C => [C1, New1, New2, New3, New4] D => [D1]
and the new overall list of entities would be:
[A1, C1, New1, New2, New3, New4, D1]
Well, that’s not quite right. It’s actually a list of lists:
[A1, [], [C1, New1, New2, New3, New4], [D1]]
and the individual lists need to be appended together to give the proper result. The append operation creates a brand new list, which means that the time and memory spent creating the individual result lists were wasted. They were just stepping stones to the real result. This almost certainly isn’t going to be a significant inefficiency, but there’s a pretty way around it: pass an accumulator list in and out of each transformation function. Now the three cases listed above neatly map to three operations:
1. To transform an entity into the next version of itself, simply prepend the new entity to the accumulator list.
2. To delete an entity, do nothing. Simply return the accumulator.
3. To create new entities, prepend each one to the accumulator.
No extra work is involved. We never build-up temporary lists and discard them immediately.
But this pretty little solution has one unintended flaw. By passing in the accumulator list, we’re giving full access to previous computations to each of the entity transformation functions. Even worse, each of these functions can arbitrarily transform this list, not only prepending values but also removing existing values or changing the order of them. (No destructive updates need occur, just the returning of a different list.) In theory we could write code that uses the list to make decisions: if the head of the accumulator is an entity of type “E,” then spawn a new entity at the same position as E. Now the entire process is order dependent…ugh.
In theory. The “flaw” here assumes that each function is going to do more than either leave the accumulator untouched or prepend values to it, that the programmer of a function may intentionally go rogue and look to sabotage the greater good. It still could open the door to bugs: imagine if a dozen people were all writing these transformation functions independently. Someone will make a mistake at some point.
Either way, the same side effects possible in imperative languages were accidentally introduced into pure functions.
Categories: Blogs Programming in the 21st Century
Erlang on Twitter
» airheadnerd (AIR HEAD NERD): @LavonWoods Then again, I figure if speed’s what you really want there’s Servlets or Erlang
» gavinlaking (Gavin Laking): RT @rsslldnphy: A blog I done wrote (http://t.co/kyXchPa5Ws) about a gem I done wrote (http://t.co/4xM4Huz288). #erlang style pattern match…
» Muh_Erlang (M. Erlangga Pangestu): RT @TeladanRasul: Tiada sesuatu yg disesali oleh penghuni surga kec 1 jam yg mrk lalui tanpa mrk gunakan utk berzikir kpd Allah Azza wajall…
» dasrecht (Bastian Widmer): Just had an #erlang Aha-Effect!
» ubahnverleih (ubahnverleih): Moment. WhatsApp Infrastruktur ist in Erlang geschrieben?
» FathurrohmanR (F A T H U R): Iya haha RT”@Erlang_07: @FathurrohmanR asikk amat tuh tur tempat y. Haha”
» forjared (Jared Rosoff): @aphyr isn’t that an erlang? http://t.co/OQgdC7CWNB
» ubahnverleih (ubahnverleih): Oder ich Schau mir Erlang an. Hust.
» huntugtweets (huntugtweets): HUNTUG meeting, tonight, May 20th, 6pm - “Intro to Erlang for C# Developers” by Ineta speaker Bryan Hunter.
» k_gadek (kgadek): Midnight. Sounds like a perfect time to grab a coffee and start writing an article about Erlang with deadline… 7 minutes ago.
Statistics
Number of aggregated posts: 10649
Most recent article: May 19, 2013
Latest comments
» Moraru on This is Why You Spent All that Time Learning to Program: It is true that computer science was a pain in the back at time that i’ve had to learn it…
» Commercial hand dryers on Couchbase Meetup at new HQ: Buy online from here where you will get so much of variety in Commercial hand dryers for people. If you…
» Fort McMurray Homes on Motivated Reasoning and Erlang vs Python vs Node: I don’t really understand why this post is motivational? I don’t even see a post, just a title. Fort McMurray…