Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I love Rich Hickey, Clojure and Haskell and I get it when he talks about functions and the unreliability of side-effecting code.

However I work in an environment where nearly all the functions I write have to read from the database, write to the database, make HTTP requests, decrement a user's balance, modify a frontend HTML component based on a click action, return different results based on the URI or the POST body. We also use PHP for the frontend, which is littered with functions like parse_str(), which modifies an object in place. All of these are side-effecting to one degree or another.

Given these constraints and the side-effecting nature of the logic I'm coding, what can I do to make my code more reliable and function-able?

share|improve this question
    
Can you provide a reference or a link to where Rich Hickey talks about the unreliability of side effects? –  Uday Reddy Mar 15 '12 at 20:21
2  
Programs without side-effects (like generating output) tend to be pretty boring –  user1249 Mar 16 '12 at 12:18
    
Functional programming is not about avoiding side-effects, but about clearly separating code with side-effects from pure code. –  Giorgio Mar 24 '12 at 21:02

3 Answers 3

I found what I think is a very interesting approach to your question in "Object-Oriented Modeling and Design", by Rumbaugh et al. The edition I have is a bit old (1991) but I think the concepts are still quite applicable. Also, I cite from this book but I guess you can find similar concepts by other authors and in more recent pubblications.

In Chapter 6, Functional Modeling, the authors explain that building a functional model is only one aspect of the analysis and design of an application. The other two models are the object model (the structure of your system, in terms of objects, their associations, and their operations / methods) and the dynamic model (systems states, events, state transitions).

The functional model "shows how output values in a computation are derived from input values" (page 123 in my 1991-edition). In other words, even though you have a lot of state (e.g. files, database) that you modify, and a lot of user input (events) that may guide your computation, you can identify the purely functional aspects in your application, with clear inputs, outputs, and data transformations, and no side effects (the side effect may consist in storing the result of a computation, but this is a separate step).

Further, on page 137, there is a section about the relation between functional, object, and dynamic models. I cite again: "The functional model shows what has to be done by a system. [cut] The object model shows the doers - the objects. The dynamic model shows the sequence in which the operations are performed."

So, these are just some rough ideas (you can do some reading on object-oriented design to have more details), but I think you can apply the following guidelines to the programming of a web application:

  • Try to identify data flows and transformations inside your application, with clear inputs (e.g. a database table) and clear output (e.g. a GUI table).
  • Clearly separate in your code the parts that (1) only read / query data (2) only write (save or update) data (3) only transform data.

All procedures that are in category (3) should

  • Either produce a completely new data object (the result) without changing the inputs or any other data in the environment;
  • or change an input data object and return it as the result (the input value is destroyed and a new output value is created; for efficiency reasons one common data object is used for both input and output), again without changing the remaining inputs or any other data in the environment.

So, IMO even if you are developing an application that has a lot of input / output with the user, other applications, and mass storage, you can still try to isolate its functional parts and implement them more cleanly.

share|improve this answer

Immutable objects go a long way to supporting no-side-effect function calls.

Here's some ways you can model immutables in your coding at various tiers:

  • PHP functions: you need to find an alternative, or write your own wrapper to make a copy of the string before updating it
  • Write to the database: don't do updates, always insert. Your select then needs to pull the latest object. Numerous ways to do this.
  • HTTP requests: depends really on the backend system, I'll assume out of scope.
  • Decrement a user's balance: Assume same as write to database. It may help to reframe mentally the "balance" as a first class attribute in your model to become a pre-calculated and cached point in time position of all prior transactions.
  • Modify a frontend component: Replace the component, don't change it.
  • Return different results based on URI: Not sure how you expect this to change; functions will perform differently depending on different data inputs.

These are just examples to get you started. The key I think is that immutables and "no side effect" transactions achieve the same outcome.

Side note - this is a powerful technique and can actually be used to avoid the need for locks in multithreaded programs but does have associated memory / garbage collection impacts. All depends what you need.

share|improve this answer
1  
I'm just curious about the 'never update, always insert' rule. Why ? –  Guillaume Mar 14 '12 at 12:36
    
@Guillaume: It is an extreme form of the Functional Programming gestalt: always use immutable objects. –  Robert Harvey Mar 14 '12 at 20:50
    
Isn't it possible to have immutable objects in code and mutable entities in db ? –  Guillaume Mar 15 '12 at 8:08
    
@Guillaume Absolutely yes. Pick and choose what you need. The poster gave an example of database access having side effects - so I've suggested an example of how to make the "normal" CRUD way of thinking of databases and make it more functional. This is not the only option either. –  jasonk Mar 15 '12 at 8:53
2  
BTW: this is exactly how "decrementing a user's balance" works in the real world. Only introductory OO textbooks have Account objects with a mutable balance field, real banking systems have immutable Transaction objects and a user's balance is simply the sum of their transactions (which, incidentally, is a fold over their transactions, with fold of course being one of the fundamental abstractions in functional programming). –  Jörg W Mittag Mar 16 '12 at 2:15

I would not agree with Rich Hickey or anybody else that claims that "side effects" (meaning state changes?) are inherently unreliable. There are several factors at play which possibly give rise to such an impression, and I will mention them as far as I understand.

  1. Programmers doing imperative programming think of it essentially as an intuitive activity, and disregard/ignore the mathematical basis of imperative programming. Programmers doing functional programming, on the other hand, keep mathematical functions firmly in mind. So, I presume that they treat it as a more mathematical activity. But, with training and practice, people can develop the same degree of mathematical rigour with imperative programming too. I know of plenty of examples of good practice in imperative programming where people use formal methods on a daily basis and build very high-quality systems.

  2. Imperative programming is harder because the problems we tackle with it are harder. Functional programming usually shuns such problems. Hence, it is easier. If functional programmers tackle the same kind of problems that we do with imperative programming (build the same kind of data structures that achieve high performance, handle state updates necessary for the real world etc.), I believe they will face the same kind of complexities that imperative programmers do. It is easy to say that functional programming is easy as long as you keep doing the easy stuff!

  3. There is at least one sense in which imperative programming is less reliable than functional programming, viz., we don't know yet how to get mileage out of type systems in imperative progrmming. You can't compose two functions unless their types match. In contrast, you can compose two commands no matter what they do. We don't have type systems that say this command achieves this, and that command requires that, and you can compose them only these specs match. We are making some progress on that front now. Microsoft has released a verification tool called Code contracts which does something akin to typechecking in the imperative programming world. It seems very successful and is becoming something of a rage in fact.

  4. Imperative programming languages are also crummy, designed by practitioners who don't know much about language design. Functional programming languages, on the other hand, are designed by programming language researchers and, so, are well-designed and well-tailored. So, we are not comparing the like with the like here. Imperative programming languages designed by researchers don't get used. They get swamped by the practitioners' languages. So, imperative programming is a victim of its own success in a sense.

The programming languages I use are what get called "functional" languages, like Lisp, Scheme and ML. You can do both functional programming and imperative programming in them, and I have done both. The problems you can tackle with functional programming are clean and easy. They are equally clean and easy even if you code them imperatively. It doesn't make much difference which style you use. On the other hand, the problems I tackle with imperative programming, functional programming cannot even begin to tackle. So, I would ignore the false prophets that sing the virtues of functional programming before they even begin to tackle the hard problems of programming.

In terms of advice, I would say, please do learn functional programming. It can clarify your thinking and make you a better imperative programmer. (In an interesting blogpost, Bob Harper, who teaches functional programming at Carnegie-Mellon recounted the feedback they got from their Facebook alumni. "They overwhelmingly said that one of the three most important courses for them was my course on functional programming. The reason? Because they learned there how to think abstractly, and learned what the code “should be”".)

Keep the specifications of the functions/procedures topmost in your mind and make sure that they match whenever you compose calls. If you can get a tool like "Code contracts" to work in your environment, do use it. In the absence of any such tools, what I do is to trace through the code line by line, with the aid of a debugging tool if I have one, and make sure that every line is doing what it is supposed to do.

When you deal with complicated data structures with lots of cached information etc, write down the invariants that govern the data structure. And, then make sure that every method re-establishes the invariant, even in the presence of exceptions. Programming data structures is the hardest part of imperative programming.

And, if you find that you are making too many mistakes and the code is turning out to be buggy, do slow down and think more carefully. If you can't get it right the first time, the problem is probably hard, and there is no use pretending that it is easy.

share|improve this answer
1  
I'm not quite sure what you mean by saying that the problems tackled by imperative programmers are "harder". I'm sure the sort of problems people working at banks or on GHC solve are as difficult as any. –  Tikhon Jelvis Mar 16 '12 at 9:01
    
@TikhonJelvis I can tell you pretty concretely what is meant by "harder". Take some data structure problem, e.g., reversing a list. Write a functional program for it and an imperative program for it. In fact, write two imperative programs, the simple one that does the same thing as the functional program and the "harder" one that reverses the list in place. You can use any programming language to write these programs, even C. You will see for yourself what is "hard" about imperative programming. –  Uday Reddy Mar 16 '12 at 9:37
1  
@UdayReddy: You might be right about functional programming not necessarily equating with parallelism, under certain localized circumstances. However, Erlang, a functional language developed by Ericcson to run massively-parallel phone switches, succeeds dramatically in that regard. Your arguments are not compelling; a functional programmer will argue that solving problems imperatively can be harder because imperative programming can be more difficult to reason about in a parallel environment, not because the problems are hard. And they'd be right. –  Robert Harvey Mar 19 '12 at 19:43
1  
@RobertHarvey: Erlang is a concurrent programming language based on message passing. It is not purely a functional programming language. Rather, its concurrency model is something intermediate between functional and imperative paradigms. While I know very well that Erlang has been designed for concurrent programming, it is yet to be seen whether it is also good for parallel processing. Here is a link to a research project which is trying to do that. Ergo, it is not a solved problem! –  Uday Reddy Mar 19 '12 at 21:59
1  
Erlang's message passing ties in perfectly with functional programming: the objects that an actor receives will not be inadvertently modified by some other concurrent process. So, FP is good for message passing. As for functional data structures, there's a lot of ongoing research into making them more performant: cstheory.stackexchange.com/questions/1539/… –  larsmans Apr 4 '12 at 10:34

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.