Think Like A Functional Programmer For Better Object Oriented Code

The benefits of functional programming have been touted up one side of the nerdosphere and down the other. But when I picked up a little F# last month, I uncovered one that wasn’t printed on the tin.

What I found was that while functional languages like F# make it easy to fall into the pit of success with FP, the languages themselves are not the gatekeepers to the mindset, techniques, or payoffs of programming functionally with a capital F. If your boss won’t let you throw down in Clojure just yet, scoot a little closer and let me show you how you can get some of that FP awesome to rub off on your scabby old C# today.

Not what they mean by overloading, you guys

So, here we have a typical function from a typical OO codebase. That stretching sound you hear is me calling this code object-oriented–this pasta is downright procedural.

public void SetPrimaryUserTo(User newPrimaryUser) 
{
    // is the current user the primary user?
    if (_currentUser.IsPrimaryUser || _currentUser.UserRole == ApplicationRoles.FirmOwner) 
    {
        // does the new primary user belong to the same firm?
        if (newPrimaryUser != null && _currentFirm == newPrimaryUser.Firm) 
        {
            // reset all users' primary flags
            foreach (User user in _currentFirm.Users) 
            {
                user.IsPrimaryUser = false;
            }
 
            newPrimaryUser.IsPrimaryUser = true;
 
            // notificate
            var notifications = new Notifier();
            notifications.Send(newPrimaryUser, "Congratulations!  You're the new primary user for your firm!  Remember: with great power comes great responsibility.");
        }
        else 
        {
            throw new Exception("User does not exist.");
        }
    }
    else 
    {
        throw new Exception("You are not authorized to do that.");
    }
}

dr-evil-function

If you squint long enough, you can make out that our thirty-odd lines of grunt here is responsible for the following.

  1. Switching a firm’s primary user status from one user to another,
  2. Making sure that the current user has the privilege to do so,
  3. Making sure that the new user exists and belongs to the same firm as the current user, and
  4. Notifying the new primary user after the switch has been made.

And boy, are its arms tired. #badumpbump

Seriously, folks. It juggles responsibilities like cloned Michael Keatons. The generic exceptions being thrown are nowhere near the inscrutable if blocks that trigger them and that first comment is a lie by omission. It smells like nobody loves it.

But what could a sagacious and popular developer such as yourself do about a tragic snippet such as this?

Make it func-y

Usually the first thing about functional programming that gets an OOP lifer’s dander up is that the variables are immutable by default. Just to underscore that: you cannot (and should not) change the value of a variable in functional programming.

How, then, does the functional programmer ever get anything done? With functions. Nootch.

let x = 9
let z = add x x
assert (18 = z)

Except now we’re not talking about functions like the homunculus Dr. Evil was taunting a few paragraphs back. Functional code would never get off the ground if that was a function. What makes FP tick at scale is that its functions are small and discrete, designed to be composed together to achieve a larger goal.

Put another way? Stop me if you’ve heard this one before. They are responsible for doing exactly one thing. That’s right–functional programming and the Single Responsibility Principle are totally sittin’ in a tree.

Keeping that in mind, let’s get our Extract Method on with SetPrimaryUserTo:

public void SetPrimaryUserTo(User newPrimaryUser) {
    if (!HasPermissionToReassignPrimaryUser(_currentUser))
        throw new UnauthorizedAccessException();
 
    if (!UsersAreInTheSameFirm(_currentUser, newPrimaryUser))
        throw new UserDoesNotExistException();
 
    AssignPrimaryUserStatus(newPrimaryUser);
    Notify(newPrimaryUser, NewPrimaryUserNotificationMessage);
}
 
private bool HasPermissionToReassignPrimaryUser(User user) {
    return user.IsPrimaryUser || IsFirmOwner(user);
}
 
private bool IsFirmOwner(User user) {
    return user.UserRole == ApplicationRoles.FirmOwner;
}
 
private bool UsersAreInTheSameFirm(User user1, User user2) {
    if (user1 == null || user2 == null)
        return false;
 
    return user1.Firm != user2.Firm;
}
 
private void AssignPrimaryUserStatus(User newPrimaryUser) {
    foreach (User user in _currentFirm.Users) 
        user.IsPrimaryUser = false;
            
    newPrimaryUser.IsPrimaryUser = true;
}
 
private void Notify(User user, string message) {
    var notifications = new Notifier();
    notifications.Send(user, message);
}
 
private string NewPrimaryUserNotificationMessage {
    get {
        return "Congratulations!  You're the new primary user for your firm! Remember: with great power comes great responsibility.";
    }
}

Of course, the FP-savvy among you are currently pushing up their Warby Parkers and calling me out on the fact that this code is not really functional. Why, there’s state change a-plenty in AssignPrimaryUserStatus, for one thing. And we’re not doing that thing where we pass functions into other functions. Or even pretending to with LINQ.

What we have done, though, is that we have taken a sizable page from the FP playbook and used it to grow our C# into more composable, more resilient (and thus more valuable) code. Still don’t see it?

Right. You asked for it.

Aaaaaand here’s the part where I throw in some new requirements to see how our code reacts

  1. Users must be Owners to be assigned primary status.
    public void SetPrimaryUserTo(User newPrimaryUser) {
        if (!HasPermissionToReassignPrimaryUser(_currentUser))
            throw new UnauthorizedAccessException();
     
        if (!UsersAreInTheSameFirm(_currentUser, newPrimaryUser))
            throw new UserDoesNotExistException();
     
        if (!IsFirmOwner(newPrimaryUser))
            throw new UserNotQualifedForPrimaryException();
     
        AssignPrimaryUserStatus(newPrimaryUser);
        Notify(newPrimaryUser, NewPrimaryUserNotificationMessage);
    }
     
    // ...
  2. The new primary user is notified when their throne has been usurped.
    public void SetPrimaryUserTo(User newPrimaryUser) {
        if (!HasPermissionToReassignPrimaryUser(_currentUser))
            throw new UnauthorizedAccessException();
     
        if (!UsersAreInTheSameFirm(_currentUser, newPrimaryUser))
            throw new UserDoesNotExistException();
     
        if (!IsFirmOwner(newPrimaryUser))
            throw new UserNotQualifedForPrimaryException();
     
        AssignPrimaryUserStatus(newPrimaryUser);
        Notify(newPrimaryUser, NewPrimaryUserNotificationMessage);
        Notify(_currentPrimaryUser, ItAintYouAnymoreNotificationMessage);
    }
     
    private string ItAintYouAnymoreNotificationMessage {
        get {
            return "Oh noes! You are no longer the primary user.";
        }
    }
     
    // ...
  3. The current primary user is notified when an unauthorized power grab has been narrowly averted.
    public void SetPrimaryUserTo(User newPrimaryUser) {
        if (!HasPermissionToReassignPrimaryUser(_currentUser)) {
            Notify(_currentPrimaryUser, FailedCoupNotificationMessage);
            throw new UnauthorizedAccessException();
        }
     
        if (!UsersAreInTheSameFirm(_currentUser, newPrimaryUser))
            throw new UserDoesNotExistException();
     
        if (!IsFirmOwner(newPrimaryUser))
            throw new UserNotQualifedForPrimaryException();
     
        AssignPrimaryUserStatus(newPrimaryUser);
        Notify(newPrimaryUser, NewPrimaryUserNotificationMessage);
        Notify(_currentPrimaryUser, ItAintYouAnymoreNotificationMessage);
    }
     
    private string FailedCoupNotificationMessage {
        get {
            return "Someone is fully out to get you. Thought you should know.";
        }
    }
     
    // ...

All right, so our app doesn’t exactly smell like team spirit. But its code sure can look change in the eye and yawn.

Even if you’re nowhere near a Haskell compiler, thinking like a functional programmer is good for you and your object-oriented code. Not only will it make your code more vital and attractive to its potential mates, but it’ll also limber up your gray matter so that when the day finally comes for you to hot swap a little Erlang, you’ll be all over it.

Advertisements

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