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?

Tagged: , , ,

§ 6 Responses to Section 7.7: Side-effects in functions

  • A.J. says:

    A Visual Studio capability allows you to write the code in C++ as well:

    class PointP
    {
    public:
       double GetTheta() const { return 0.0; }
       __declspec(property(get = GetTheta)) double Theta;
       double GetRho() const { return 0.0; }
       __declspec(property(get = GetRho)) double Rho;
    };
    
    class PointF
    {
    public:
       double Theta() const { return 0.0; }
       double Rho() const { return 0.0; }
    };
    
    int main()
    {
       PointP ppoint;
       PointF fpoint;
       return ppoint.Theta == fpoint.Theta();
    }
    

    Also I’m pretty sure when creating an object in C# its gotta be “var ppoint = new PointP();” with the “()”. Say in C# are you not allowed to make a method const (or readonly)? If not: booo!

    Furthermore, me and my boys have been using an essentially equals:
    bool feq(float f1, float f2) { return ::fabs(f1-f2)<1e-7; }

    Eiffel! Geeze! Soon enough there won't be any program languages you don't know or have tried (at least of any remotely popular ones).

    Good post Jared!

  • msujaws says:

    Thanks, great comment! Those declspec functions are unique to MSVC though, but are nonetheless relevant to the topic.

    Yes, you’re right. Parens are required for constructors in C#, I’ve fixed that now. I think I made the mistake of using too many different programming languages for this post. :)

    The function that you are using to compare floats is pretty close to my favorite way that it is done. I like the way that a lot of unit testing frameworks have Equals defined as.

    public static class Test {
       public static bool Equals(int first, int second) { return first == second; }
       public static bool Equals(float first, float second, float delta) { return Math.Abs(first - second) < delta; }
    }

    Leaving the delta open as an argument allows the caller to be more precise when necessary.

    I picked up this book because I had heard it was one of the greatest books to learn object-oriented programming from, and I must say that the statement isn’t too far off. Eiffel is a great language, and some of its features like Design by Contract, sole reference semantics, garbage collection, and more make Eiffel really fun to program in. But one of the major downsides is that it lacks a solid library like the .Net BCL.

  • A.J. says:

    I am thinking if you make the second equals be <= instead of < you could have one call the other; and if you really want == you can just use == instead of the Equals function:

    public static class Test {
    public static bool Equals(int first, int second) { return Equals(first, second, 0); }
    public static bool Equals(float first, float second, float delta) {
    return Math.Abs(first – second) <= delta; }
    }

    I am assuming two things: one, this is unit tests so efficiency isn't too important, and two the compiler could help if you are doing unnecessary operations.

  • msujaws says:

    I think this is getting a little away from the meaning of the post, but I am concerned at the idea of pushing integer equality through a floating point conversion.

    For example in Python:

    frep = float(10000000000000000000001)
    assert.isFalse(10000000000000000000001 == frep)
    assert.isTrue(1e+22 == frep)
  • Karl Rosaen says:

    Interesting post. If you haven’t yet, you might check out http://clojure.org and its approach to this problem http://clojure.org/state, http://blip.tv/file/812787. Everything is immutable (and the data structures are implemented cleverly enough to make this efficient), but when you need to model a changing view of state, it provides a lot of cool built in features that help you do so explicitly and safely. I’ve played with it some, but haven’t had a good enough reason to use it seriously yet.

  • msujaws says:

    I’ve heard a lot about Clojure and should take some time to play with it. Lisp has been making such a strong comeback with the move to multi-core machines and the abilities that functional programming languages can use to parallelize operations.

    Looking forward to understanding Clojure’s approach to this problem! Sounds like it might be a good future post :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

What’s this?

You are currently reading Section 7.7: Side-effects in functions at JAWS.

meta

Follow

Get every new post delivered to your Inbox.

Join 1,007 other followers