Section 7.7: Side-effects in functions

30 December, 2009 § 6 Comments

I’ve been reading Bertrand Meyer’s Object-Oriented Software Construction recently and have enjoyed learning a bit about the Eiffel programming language (also written by Meyer) at the same time.

The book focuses on principles of OOP (such as modularity and re-usability), techniques (such as generics), and also discussions such as genericity versus inheritance.

While there are many great topics covered in the book, I wanted to first write a blog post about ยง 7.7: Side-effects in functions.

Side-effects in functions

C#, much like Eiffel, has the notion of properties and functions. Both of these can be used the exact same way. For example, a Point class may have a property Theta or a function Theta:

// C#
public class PointP {
   public double Theta { get { return 0.0; } }
   public double Rho { get { return 0.0; } }
}

public class PointF {
   public double Theta() { return 0.0; }
   public double Rho() { return 0.0; }
}

public class Test {
   public static void Main() {
      var ppoint = new PointP();
      var fpoint = new PointF();
      Debug.Assert( ppoint.Theta == fpoint.Theta() );
   }
}

While both can accomplish the same goal, there are semantic differences between the two. Meyer's espouses that properties should be thought of as simple accessors, while functions may be thought of as mutators. Within Eiffel, this is referred to as the Command-Query Separation.

Therefore, functions like C's getint() violate this principle, since they return the last integer seen on the input stream and advance the cursor at the same time. getint() is therefore an accessor and a mutator.

Accessors should have the property that they can be called multiple times with the same output each time.

Functions, in their most mathematical sense, should have the property that they can be called multiple times given the same input and have the same output each time.

In this example, Meyer would have written something like this:

/* C */
FILE input;
input.advance();
int lastSeenInteger = input.lastint;
ASSERT( 2 * input.lastint == input.lastint + input.lastint );

The same cannot be said for getint().

While we have now achieved an arguably better solution than what existed before, Meyer leaves the reader to decide if the function should mutate itself or return a mutated object. I will take this opportunity to conclude on his discussion.

Taking away the guesswork

At this point, it is up to the programmer to know if the function is going to mutate itself or return the mutated object. The programmer may check the interface of the class explicitly, or use features of their IDE to tell them if this function returns a void or a separate FILE reference.

By convention, Ruby solves this problem by using an exclamation point at the end of the function name to be explicit that the function will change the internal state of the object.

# Ruby
input_b = input_a.advance
b_last_int = input_b.lastint
input_a.advance!
a_last_int = input_a.lastint
assert( a_last_int == b_last_int )

Conventions like these remove guesswork out of the equation and allow the developer to know when they are calling a function with side-effects.

Readability is key

In the end, we as software developers should strive for the most readable and maintainable code. Each one of these tools, like side-effect function conventions in Ruby, should allow us to achieve this goal.

Do you have any conventions that you use or know of that call out side-effects in functions?

Where Am I?

You are currently browsing entries tagged with ruby at JAWS.

Follow

Get every new post delivered to your Inbox.

Join 982 other followers