Sunday, March 20, 2016

Generic Programming and POP with Swift – The Law of Useful Return

In my first post on Generic Programming I talked about the iterative approach to improving algorithms.  In this post I would like to discuss another concept from the From Mathematics to Generic Programming book that I am currently reading. This concept is called The Law of Useful Return.

The Law of Useful Return says:

If you have already done the work to get some useful result, don’t throw it away.  Return it to the caller because they may be able to use it.

What do we mean by this law.  Well the easy way to explain it is to look at an example.  In the From Mathematics to Generic Programming book, they explain this law based on a mathematical equation however, most developers that I know do not get that excited about complex mathematical equations therefore I am going to take a non-mathematical approach to explaining this.

Lets say that we were creating a pump that is connected to the Internet for real time monitoring (think IoT).  This pump could be used to pump different types of liquids and would be able to return the temperature of the liquid as it is being pumped.  In our application, that monitors the pump, we could then retrieve the temperature and send out an alert if it is outside of the acceptable range for the particular liquid that is being pumped.  Our function to monitor the temperature may look something like this:

func isTemperatureWithinRange() -> Bool {
    //Get liquid temperature
    var temp = getLiquidTemp()
   
    //Check if temp is within acceptable range
    if temp < MIN_TEMP || tem > MAX_TEMP {
        return false
    }
    return true
}

This function would work well and if the temperature was outside the acceptable range it would return false triggering an alert.  The problem that could arise from this is eventually we will want to display the actual temperature of the liquid being pumped, therefore someone may add another function that would simply retrieve the temperature of the liquid and return it.  This function may look something like this:

func getTemperature() -> Double {
    //Get liquid temperature
    return getLiquidTemp()
     }

Now in order to retrieve the temperature of the liquid and to also check if it is within the acceptable range we would need to call the getLiquidTemp() function twice.  Since this function retrieves the temperature from a remote device, making two separate calls like this is definitely not optimal.  What we could have done to avoid this problem was to return the temperature with the Boolean value that indicates if the temperature was within the acceptable range.  It would be very easy to do since we are already retrieving the temperature as part of the check.  The function that returns both values could look something like this:

func getTemperature() -> (acceptable:Bool, temperature: Double) {
    //Get liquid temperature
    var temp = getLiquidTemp()
   
    //Check if temp is within acceptable range
    if temp < MIN_TEMP || tem > MAX_TEMP {
        return (acceptable:false, temperature: temp)
    }
    return (acceptable:true, temperature: temp)
}

Be careful not to follow this law to closely because we only want to return data that is useful within our application.  When we create functions like this we really need to ask ourselves not only what information do we currently need but also what information may be useful.  In our example above, returning the temperature, since we already retrieved it, could definitely prove useful even if it is not needed with our current requirements.  Writing our API correctly (generically) the first time will save us from having to change the interface for our APIs in the future.  This means a lot less refactoring of our code.

To me, one of the ideas behind the Law of Useful Return is to avoid making our APIs so granular that we end up performing the same functions multiple times.  To illustrate this idea another way lets look at a second example.  Lets say that we have a NSDate object and we need to retrieve the month from it.  We could very easily design our API like this:

func getMonth(date: NSDate) -> Int {
    let components = NSCalendar.currentCalendar().components(.Month, fromDate: date)
    return components.month
}

This works great for our present need however what happens if in the future we also need to retrieve the day of the month.  We could write a second function to retrieve the day of the month however we would then need to make two function calls if we needed both the day of the month and month itself.  A better way to design our API is to think ahead and realize that if we are retrieving the month from the NSDate object maybe the day of the month and year would be useful.  We would then create a function that returns the month, day and year like this:

func getDate(date: NSDate) -> (month: Int, day: Int, year: Int) {
    let components = NSCalendar.currentCalendar().components([NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.Year], fromDate: date)
    return (month: components.month, day: components.day, year: components.year)
}

By designing our API to return the month, day and year rather than just the month our function becomes much more generic and useful not just for our present needs but also for our future needs.

When we create APIs that only meet our present requirements we end up having to do a lot of extra code refactoring in the future when we have to change those API to meet our future needs.  When we are designing our APIs we should always remember to think beyond our present needs and think about how we can make our APIs generic enough to also meet our future needs.

There are going to (hopefully) be a number of posts in the Generic Programming and POP with Swift series therefore I made a separate post that contains links to all of the articles in this series.  The post is located here:  http://masteringswift.blogspot.com/2016/03/generic-programming-and-protocol.html


No comments:

Post a Comment