Over a million developers have joined DZone.

Functional Programming: Preserving Type Safety

· DevOps Zone

The DevOps Zone is brought to you in partnership with Sonatype Nexus. The Nexus Suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

Overview

Functional programming is a development paradigm centered around the use of functions. It's not just a matter of using functions, but about the perspective and thought process you exercise when faced with a programming problem. In other words, it is a framework for the mind and your code.

The goal of FP is to abstract operations on data with functions in order to maximize reusability and reduce mutation of state and side effects in your application. It so happens that by doing this, your code is much more scalable, readable, and testable.

Functional programming demands that functions have 2 important qualities:

  1. Be first-class citizens
  2. Be of high order
Functions are first-class when they can be created and assigned to a variable. Functions are high-order when they can be used in combination with other functions whether it is passed in as an argument or returned from a function. Let's expand this notion further. When a function is equivalent to a value, it follows a principle of substitutability, which means that a function call's return value can be replaced and inlined into an expression without altering its result. Consider a quick JavaScript example:

   function areaSquare(a) {
        return a * a;
   }

   function volumeCube(areaOfSquare, a) {
        return areaOfSquare * a;
   }

   a = 2;
   area = areaSquare(a);         // -> 4
   volume = volumeCube(area, a); // -> 8

The following expressions are equivalent:

   volume = a * a * a; 

   volume = areaSquare(a) * a; 

   volume = volumeCube(areaSquare(a)); 

Why JavaScript?

Simply put, omnipresence. JavaScript is everywhere. For many years, the language of the web, and now it's also breaking into the server market. Most importantly, JavaScript is a functional language; it actually supports writing in both functional and object oriented styles. This, I argue, should be the only way in which we should be using JavaScript.

Furthermore, JavaScript is a non-statically typed language. Though types are enforced, it is done at runtime and not at compile time. In fact you don't include type information in your code. Considering the invariant that referentially transparent functions need to be predictable and return the same value on same input when called, it follows naturally that functions need to be consistent in the type of value or object they return. Referentially transparent functions have no side effects and depend only on the input provided.

Functions like:

Date.now();

and

   var counter = 0;                    // global
   function incrementCounterTo(num) {
 return counter += num;    
   }

are not considered pure due to side effects that break referential transparency.

Preserving Type Safety

In order to talk about the issue of type safety, consider this code:

   var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                 "Aug", "Sep", "Oct", "Nov", "Dec"];
   function getMonthName(mo) {
      if (months[mo - 1] !== undefined) {
         return months[mo - 1];
      } 
      else {
         throw new Error("Invalid Month Error!");
      }
   }

It's clear that for every valid month number, this function will always return same month name, which is string. However, for other inputs (mo > 12) this function throws an exception, so type is not preserved across input values in the domain of this function. Exceptions causes the stack to unwind and set external program flags for termination, which cause side effects to occur. Calling this code looks like the following:

    try {
        var m = getMonthName(13);

        // do work with m
    }
    catch (e) {
        return e.message;
    }  

Also, this function violates referential transparency, for you can't under any circumstances inline an exception statement as part of a bigger expression, as it will alter the entire meaning of the expression.

Let's look at a more functional approach that will solve all of these issues.

Optional

The solution is based on a core functional programming concept called monads. In particular, we will be dealing with the Optional monad, also called the Maybe monad in some functional programming languages. Here is the code for the Optional JavaScript object (some details omitted for brevity):

var Optional = (function () {

    // private constructor
    function Option(val) {
        var _value = val || null;

        // public methods

        this.get = function () {
            if (!this.isPresent()) {
                throw 'NoSuchElementError';
            }
            return _value;
        };
        
        this.map = function (fn) {
            if (!this.isPresent()) {
                return Optional.empty();
            }
            if (typeof fn == 'function') {
                return Optional.ofNullable(fn.call(_value));
            }
            return Optional.ofNullable(_value[fn].call(_value));
        };

        this.getOrElse = function (other) {
            return _value !== null ? _value : other;
        };
    }

    return {

        empty: function () {
            return Object.freeze(new Option());
        },
        
        of: function (val) {
            if (val === null) {
                throw 'NoSuchElementError';
            }
            return Object.freeze(new Option(val));
        },
        
        ofNullable: function (val) {
            var inst = val !== null ? this.of(val) : this.empty();
            return Object.freeze(inst);
        }
    }
})();

There are many ways of doing this, I like the notion of immutability in objects, which is why I choose to freeze objects as they are created. Using Optional we keep referential transparency as well as preserve type safety in our functions. This has many benefits:

  1. Create a clear contract for our functions
  2. Prevent our callers from having to place boilerplate exception handling code into every call
  3. Provide a more succinct and fluent API experience

Let's look at how we can improve our sample problem:

   function findMonthName(months, mo) {
        if (months[mo - 1] !== undefined) {
            return Optional.of(months[mo - 1]);
        }
        else {
            return Optional.empty();
        }
    }

This allows you to be more idiomatic about how you handle failures. Your calling code looks like:

findMonthName(months, 13).getOrElse('No month found');

As you can see this function keeps its contract by returning an Optional object to encapsulate success and failure states.

Here is another use case for this functional pattern. Consider the following simple objects:

    function Address(country) {
        var _country = country;

        this.getCountry = function() {
            return _country;
        }
    }

    function Person(addr) {
        var _address = addr;

        this.getAddress = function() {
            return _address;
        }
    }

    function Customer(person) {
        var _person = person;

        this.getPerson = function() {
            return _person;
        }
    }

It would be nice to be able to access the address of a customer as:

customer.getPerson().getAddress().getCountry()

And expect it all to work all the time.....Right! You will probably want to guard against this access. In imperative and object-oriented programming, you will be writing code like this:

    var p = customer.getPerson();
    if(p !== null) {
        var a = p.getAddress();
        if(a !== null) {
            var c = a.getCountry();
            return c;
        }
        return null;
    }
    return null;

Can you spot any issues with this code? For starters, the incessant null checking that occurs only to return null again because there is no other sensible value. So, the caller of this function also must perform another round of null checks.

Let's now see a more scalable and idiomatic approach, the functional way:

   customer.getPerson()
            .map('getAddress')
            .map('getCountry')
            .getOrElse('Unknown');    

If any value is missing in the chain of calls, this will short-circuit and return 'Unknown'. We do this to avoid all of the null checks. All we need to do is tweak our API based on Optional:

   function Customer(person) {
        var _person = person;

        this.getPerson = function() {
            return Optional.ofNullable(_person);
        }
    }

And that's it!

Functional programming is a very important and powerful way of programming. Experts in the field have realized that applications written in a functional style tend to be more expressive, readable, and correct. Referentially transparent functions can have their result values substituted inline in place of the actual function call. Finally, using Optional can enhance your code in many ways by making it shorter to write as well as fluent.

The DevOps Zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

Topics:

Published at DZone with permission of Luis Atencio, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}