English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Developing a Functional Programming Edge

Creational Patterns

Creating objects seems like the simplest of things to beginner programmers. With experience engineers realize that there can be a number of tricky aspects to the creation of objects. Sometimes the creation can be very complex. Sometimes you need to limit the number of instances of a given type are created. Object-oriented programming brings its own wrinkles. Highly abstracted code can be written to work without detailed knowledge of objects, and so being able to create such objects without detailed prior knowledge about them becomes valuable. That is one of the ideas behind the first of the creational patterns we will look at, the factory method.

Factory Method

The Factory Method patterns allows you to define an interface for creating various subclasses of a given class. Let's use an example to explain what this means in practice. For our example, we will create an abstraction for different types of polygons. We will then create a factory that will take the vertices of a polygon and create the appropriate type of polygon. We will show this using classical object-oriented programming first and then examine how to accomplish the same thing with a functional programming approach. The first thing we will need for our example is a class to represent a point in two-space.

class Point(var x:Double, var y:Double){
    import java.lang.Math.sqrt
    def distanceTo(p:Point) = {
        var dx = x - p.x
        var dy = y - p.y
        sqrt(dx*dx + dy*dy)
    }
}

  

Our class has an x and y coordinate and defines a single method, distanceTo. This gives us the distance between our point and some other point p. Now let's define our interface for polygons.

trait Polygon{
    def numSides:Int
    def perimeter:Double
}

Our interface is very simple, saying a Polygon has two methods numSides and perimeter. We could add more methods, like the area, an iterator of its vertices, or the coordinate of its center, but this is good enough for our example. Now let's define a couple of implementation of this interface.

class Triangle(var a:Double, var b:Double, var c:Double) extends Polygon{
    def this(x:Point, y:Point, z:Point) = 
        this(x.distanceTo(y), y.distanceTo(z), z.distanceTo(x))
    override def numSides = 3
    override def perimeter = a + b + c
}

class Quadrilateral(var a:Double, var b:Double, var c:Double, var d:Double) 
extends Polygon{
    def this(x:Point, y:Point, z:Point, w:Point) = 
        this(x.distanceTo(y), y.distanceTo(z), z.distanceTo(w), w.distanceTo(x))
    override def numSides = 4
    override def perimeter = a + b + c + d
}

Now we have two types of Polygons, a Triangle and a Quadrilateral. Notice that both of these have their own internal implementation based on the length of each of their sides. They both provide a constructor that uses Points as well though. Now we can define a factory for Polygons.

class PolygonFactory{
    def createPolygon(v:List[Point]):Polygon = {
        var p:Polygon = null
        if (v.size == 3){
            p = new Triangle(v(0), v(1), v(2))
        } else if (v.size == 4){
            p = new Quadrilateral(v(0), v(1), v(2), v(3))
        }
        p
    }
}

Our factory method takes a List of Points and returns some type of Polygon. If we have three vertices, then it invokes the constructor for a Triangle. If there are four vertices, it invokes the constructor for a Quadrilateral. This is the essence of the pattern: an interface that defers the construction of our objects to subtypes. This is hidden from the user of the factory. This is completely hidden to the users of PolygonFactory:

object App{
    def main(args:Array[String]){
        var points = List(new Point(0.0, 0.0), 
            new Point(3.0, 0.0), new Point(0.0, 4.0))
        var p = PolygonFactory.createPolygon(points)
        println("Number of sides = " + p.numSides)
        println("Perimeter = " + p.perimeter)

        points = List(new Point(0.0, 0.0), new Point(3.0, 0.0), 
            new Point(3.0, 4.0), new Point(0.0, 4.0))
        p = PolygonFactory.createPolygon(points)
        println("Number of sides = " + p.numSides)
        println("Perimeter = " + p.perimeter)
    }
}

Here we have a simple command line application with a main method. It creates different lists of vertices and passes them to the PolygonFactory. The factory method returns a Polygon, but the caller doesn't know or care what subtype is returned. Instead it only needs the methods defined on the Polygon interface and invokes them as needed. If you have done a lot of object-oriented programming, there is a good chance that you have seen or used this pattern.

Now let's look at how this would be done using functional programming.

Doing it with FP

Let's start by re-examining our types.

case class Point(x:Double, y:Double)

case class Triangle(a:Double, b:Double, c:Double)

case class Quadrilateral(a:Double, b:Double, 
    c:Double, d:Double)

There is a lot to notice here. First we are using case classes. These are somewhat like structs in C. They are an example of algebraic data types. They can be though of as wrappers around simple data types and other algebraic data types. In our example, our Point is simply the x and y coordinates, and our Triangle and Quadrilateral are just the lengths of their sides. Our case classes have no methods, unlike traditional objects. With no methods, there is no Polygon interface at all. Instead we keep these as separate functions:

object Polygon{
    import java.lang.Math.sqrt

    def dist(a:Point, b:Point) = {
        val dx = a.x - b.x
        val dy = a.y - b.y
        sqrt(dx * dx + dy * dy)
    }

    def numSides(p:Any) = 
        p match {
            case t:Triangle => 3
            case q:Quadrilateral => 4
            case _ => 0
        }

    def perimeter(p:Any) =
        p match {
            case t:Triangle => t.a + t.b + t.c
            case q:Quadrilateral => q.a + q.b + q.c + q.d
            case _ => 0
        }
}

Here we have defined a function dist for calculating the distance between two Points. We have also defined two functions for giving the number of sides or the perimeter of a polygon. These functions use pattern matching, another important concept in functional programming. Pattern matching syntax in Scala is similar to the switch/case statement found in most imperative languages. However we are comparing types, not just integers in our match statement. We match against the type of object, and then perform the appropriate calculation. Notice that in each match statement we have a default case (denoted with the underscore) as well. Using algebraic data types and pattern matching together is a very common and very powerful pattern in functional programming. We still haven't addressed how to implement a factory method, but you might be able to make a good guess at this point:

object Polygon{    
    def createPolygon(xs:List[Point]) =
        xs match {
            case x::y::z::Nil => Triangle(dist(x,y), 
                dist(y,z), dist(z,x))
            case x::y::z::w::Nil => Quadrilateral(dist(x,y), 
                dist(y,z), dist(z,w), dist(w,x))
            case _ => xs
        }
}
  • Here we are once again use pattern matching. This time it's a little fancier. We match the List against a structure.
  • The first case is a list of three objects with the Nil list terminating the list. You can consider this as a way of saying it's a List of length 3.
  • However, since we have matched to the elements (x, y, and z) in the list, we can then refer to them when creating our Triangle. Notice that we don't use the new keyword for creating a Triangle, this is the norm for case classes. This kind of pattern matching is referred to as deconstruction. We use the same technique for Quadrilaterals.
  • Finally we have a default case where we just return the List of Points.

Now let's take a look at how we would use this more functional version of a factory method.

object App{
    def main(args:Array[String]){
        import Polygon._
        val t = createPolygon(Point(0.0,0.0) :: 
            Point(3.0, 0.0) :: Point(0.0, 4.0) :: Nil)
        println("Number of sides = " + numSides(t))
        println("Perimeter = " + perimeter(t))

        val q = createPolygon(Point(0.0,0.0) :: 
            Point(3.0, 0.0) :: Point(3.0, 4.0) :: 
            Point(0.0, 4.0) :: Nil)
        println("Number of sides = " + numSides(q))
        println("Perimeter = " + perimeter(q))
    }
}

The usage is very similar to the object-oriented version. Our factory allows us to delegate the creation of the Triangle and Quadrilateral. We are then able to pass the result of the factory method to the numSides and perimeter functions. Obviously the usage here is slightly different, but it allow a similar pattern to the classical OOP variation. The key things to take away from this are algebraic data types and pattern matching. Many creational patterns can be expressed using these two key features of functional programming languages.

Now let's look a more nuanced pattern that is very popular in OOP, the Singleton Pattern.

Singleton

The Singleton Pattern is one of the most beloved patterns in object-oriented programming. Countless developers have had to implement a singleton in pseudocode on a white-board as part of their interview process. At the same time, the pattern is also one of the most despised patterns in OOP. It essentially creates global state and global functions on that state. This is not only arguably anti-OOP, but leads to many practical headaches. Systems with singletons are infamously difficult to test. The technique of dependency injection is often touted as a way to have a testable system with singletons. It suffices to say that developers have a love-hate relationship with this pattern. So let's take a look at a singleton implementation in Scala.


The Singleton Pattern is one of the most beloved and most despised patterns in OOP.

class SysConfig private(val path:String){
    private var configValues = new HashMap[String,String]

    Source.fromFile(path).getLines().foreach(line => {
        var index = line.indexOf("=")
        var key = line.slice(0,index)
        var value = line.takeRight(line.length - index - 1)
        configValues.put(key,value)
    })

    def getValue(key:String) = configValues(key)    
}

In our example we have a singleton called SysConfig. It reads data from a configuration file and the stores it in a hash-map. The private modifier before the parameter list in the class declaration indicates that the class has a private constructor that takes a string called path. The next several lines of code are part of this constructor. We use some Scala convenience APIs to read the file, break it apart line by line, and pass each line to an inline function (a closure). This function breaks each line apart at the equals sign, creating name-value pairs that are added to the hash-map. Finally our class declares a single method, getValue that returns the value associated to a key from the configuration file. Now since our class only has a private constructor, we need some way to access it and complete the Singleton Pattern:

object SysConfig{
    private val instance = new SysConfig("sys.config")    
    def getInstance = instance
}

In many OOP languages like C++ or Java, we would use a private static instance of SysConfig along with a static method to access it. Scala does not have statics, but using an object accomplishes the same thing. Our SysConfig object has a private instance of the SysConfig class and a getInstance method that provides a handle to the private instance. Now we can use our SysConfig singleton in the typical way:

object App{
    def main(args:Array[String]){
        println(SysConfig.getInstance.getValue("foo"))
    }
}

Here we simply use getInstance to get to our single instance of SysConfig, and then we can invoke its getValue method to read configuration data from our file.

Doing it with FP

Now let's take a look at more functional version of this pattern.

object SysConfig{
    private lazy val configValues = 
      Source.fromFile("sys.config").getLines()
        .map{ line => line.split("=") }
        .map{ array => array(0)->array(1) }
        .foldLeft(new HashMap[String,String]){ 
            (map, mapping) => map += mapping 
        }

    def apply(key:String) = configValues(key)   
}
  • A Scala object can be thought of as a singleton in its own way, as these take the place of statics. So we can directly place our class code into an object. We have basically moved the code that was in our SysConfig class into the our SysConfig object.
  • Objects are typically used to hold functions, though they can also hold state. In this case we have our configValues map, just as before. However we are creating it in a single expression and declaring a lazy value. This means that the expression that defines the configValues value will not be evaluated until it is needed, i.e., not until it is accessed via our access method (more on that shortly.)
  • To do this we must express configValues as a single expression to be evaluated. Our expression starts off by getting the lines of the file, just as we did before. However instead of imperatively dealing with each line, we perform a transforming, mapping each line to the result of invoking the split method of each line. This produces an array of strings, with the string before the equals sign in the 0-slot and the string after it in the 1-slot. This is done for each line, i.e., each string in the sequence of strings produced by getLines() is replaced by an array.
  • Now we perform another transformation on this sequence of arrays. We map each array to a pair of strings. The arrow (->) is syntactic sugar for creating a tuple, or more specifically in this case a Tuple2[String,String]. So now we have a sequence of string tuples.

We transform this sequence into a map by using a fold. A fold works by taking an initial expression and then applying a folding function to the initial expression and the first element. The result is then passed to the folding function along with the second element, etc. until all elements are exhausted and the final result is returned. In this case, our initial expression in a new HashMap. Our folding function takes the HashMap and adds a tuple to it, i.e., adding a mapping of the first element of the tuple to the second element of the tuple. After transversing all of the elements in the sequence we will be left with a HashMap that contains a mapping for each name-value pair from our configuration file.

This may seem like a more complex way to create a HashMap, but it is simply a more functional way. We are specifying a series of transformations (functions) to apply to the input data to produce the desired output data structure. This type of specification really pays off because it allows us to declare the HashMap as a lazy val. The configValues value is created once and cannot be reassigned once it is create (val is like a const expression in C++ or a final declaration in Java) and its creation is deferred until it is needed. Lazy expressions are very common in functional programming, but these obviously require an expression in the first place.

Finally you probably noticed that we changed the name of the method used for accessing the configuration data. We have given it the seemingly strange name of apply. This is a special name in Scala (and many other functional languages) that allows us to treat the SysConfig object as a function. So now our usage code is as follows:

object App{
    def main(args:Array[String]){
        println(SysConfig("foo"))
    }
}

Notice how we do not use the apply method name at all, and simply provide the parameter list to the SysConfig object directly. We are able to treat it exactly as a function, and this is the true translation of a singleton. The methods of our singleton class are now simply functions. There is no SysConfig singleton, there is a SysConfig function instead.

We have now looked at a pair of the classical creational design patterns and seen how they translate to functional programming. Along the way we have been introduced to several key concepts of functional programming: algebraic data types, pattern matching, lazy expressions/values, and function objects. We have only scratched the surface and yet just these concepts provide us with very different ways to attack common programming problems. Now let's take a look at some structural design patterns and how they exist in a functional world. We will see some of the same functional programming concepts we have encountered so far, but some new ones as well.

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.