CSC/ECE 517 Fall 2012/ch1 1w8 aa

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

Functional Programming

Functional programming treats computation as the evaluation of mathematical functions. It is compute-centric rather than data-centric. Functional programming keeps no global state, does not use for or while loops, and data is immutable (i.e. functional programming uses persistent data structures). Instead, everything is done using functions, usually functions that evaluate mathematical expressions. Each of these functions maintain no internal state and will always produce the same output given the same input.

Let's look at an example. Suppose we have a list of numbers and we want to multiply each number in the list by 2. The first approach is the imperitive style:

    array[] = {1,2,3,4,5}
   for(i=0 ; i<array.length; i++){
      array[i] = array[i] * 2;
   }

In the above example, array[] is a global variable. If we fail to check the array length, then a runtime exception occurs which might result in the program being crashed. As far as functional style, there is a function called map(func,seq)(in python language) where the function 'func' is applied to each member of seq. The following code is written in a functional style.

     numbers = [1,2,3,4,5]
    /*  define a method called compute on each number in the list */
    def compute(var):
        var = var * 2
        return var
    /* Now call the map function */
    L = map(compute,numbers)
    print L                   // L will print [2,4,6,8,10]

Notice that there is no global data nor any nested loops and the bound checking is handled internally within the map function. The programmer is relieved of of runtime errors and thus can debug the program easily.

Some advantages of this approach are:

  • Easier debugging: Since every thing is broken down into small simple functions, the functions are often easy to debug.
  • More compact code: Functional programming typically requires fewer lines of code. Since compution is done by function code reuse is prevelent.
  • Nothing depends on state: Because functional programs keep no state information, the functions will always operate the same regardless of the past history of the program.

Object-Oriented Programming

Mixing Functional and Object-Oriented Programming

Motivation for Mixing

Functional Techniques useful for Object-Oriented Programming

Lambda

Currying

Pattern Matching

Examples of Mixing

Scala

Ruby

Clojure

Conclusion

References

Mixing Functional & O-O code

This chapter deals with the mixing of functional and object-oriented programming paradigms. We start with listing the different programming paradigms available today. We introduce functional and object-oriented approaches briefly and then discuss the advantages of one over the other. Following this, we discuss some aspects and features of mixing both the approaches. Finally, we talk about few languages that constitute such a mix.

Programming Paradigms Today

Several programmatic approaches to solve a particular task lead to distinct programming paradigms like,

Lets focus on functional and object oriented programming approaches.

Functional Programming

Functional programming is the task of decomposing a problem into a number of functions. Selectively executing these functions results in the solution to the problem. The functions mostly behave as mathematical. For given set of inputs the program should always return the same output. It usually concentrates on what type of problem we are dealing with rather than on the details of how to solve the problem. For example lets say there is a function which calculates area of a square. If we use it six times then we end up with area of a cube. Thus functional programming helps to build complex systems.

Approach

Expressions are the collection of variables and operations resulting in a single value this also deals with the way of solving the problem as expressions. Hence also called Expression related programming. It is structured as expression where each expression can be encapsulated in another yielding the result. Making the change in data structures as the program runs is called side effects. Purely functional language has no side effects. Language like Haskell is a pure functional language . Some languages need many approaches for achieving the desired result. Such languages are multi-paradigm. Examples for such languages are C++, Python,Lisp. Its like evaluating an expression and use the resulting value for something.

Structure of functional programming :

 (fn2 ( fn1 ()[input list] ) [])  => fn1 takes input list and the output of fn1 is used as input to fn2. Its a schematic representation of  
                                     how functional programming works.

Functional programming feature (Python) :

 m = map(lambda i : i*i , numbers)

where numbers is an array of numbers and lambda is closure.

Python has some functions like map, filter, reduce to support functional programming style. In the example above, the 'map' is a high-order function that takes a function and an array of numbers as input. The lambda takes i as input and returns i*i as output. Every result returned is appended to a list and produced as output. In functional programming, the programmer is given a natural framework for developing smaller, simpler, and more general modules, which are then glued together.

Object Oriented Programming

Object-oriented programming is a programming technique where everything is defined as an object. Objects are well-defined discrete bundles of data(attributes) and the associated functions(behavior). They are devised to resemble real-life objects and are uniquely identifiable by a name. The attributes could be of simple datatype(int, String, Boolean) or objects themselves. The behavior is defined by methods to use and manipulate the attributes. The values of attributes of an object at an instant is the state of the object. OO Programming allows objects to have their state retained throughout the code until manipulated otherwise. Objects are capable of housekeeping their own states, interacting and establishing relationships with other objects, inheriting, etc.

Fundamental concepts

A class is a user-defined datatype that provides implementation details to define the attributes and their properties. It is an abstract representation of an object(i.e.,) like a mold for objects. An instance is an executable copy of a class. They are the actual objects created using the ‘class mold’ on run time. Methods are a set of procedural statements or functions to define the behavior of an object, convey the current state, modify values of the attributes, etc.

Example :

An object in Java might look like this:

public class Person {

 private String name;
 public Person() {
 }
 public void setName(String name) {
     this.name = name;
 }
 public String getName() {
     return this.name;
 }

}

Here, OO Programming allows the programmer to set the name attribute of the class Person and get the name whenever needed in the code. An object of this class Person will remain in memory forever as long as there is a reference to it. As OO Programming allows objects to have their state retained, the value of the name attribute for a particular Person object remains the same across any part of the code until it is set differently.

Features

Three primary characteristics of object oriented programming are Encapsulation, Polymorphism, and Inheritance. Encapsulation is the ability of objects to hide visibility of their attributes and behavior, thereby providing service independent of implementation. Polymorphism allows identical operations to behave differently in different contexts. The operations that can be performed on an object make up its interface. They enable addressing of operations with the same name in different objects. Inheritance is one where an existing type can be used to derive a new type. Derived types inherit the data and operations of the super-type. And also, they can overwrite existing operations or add new ones.

There are other important features of O-O programming like message passing, abstraction, delegation, etc.

Now, we have seen both the approaches and their features in brief. Lets discuss the advantages of one over the other.

Advantages

Functional Approach over OO Approach[8]

  • Functional programming solves the problem with lesser number of lines of code than the OO programming style.
  • There is no side effect as they use immutable data structures also called Persistent Data Structure which will have no change in the state of variables. Hence program dont have to depend on history.
  • There is no limit in the numeric types used as opposed to OO languages which mainly depend on primitive data types.
  • A programmer is given the freedom to think in terms of what to solve in the problem rather than concentrating on how to solve and the flow of the program.
  • Suitable for calculation intensive programs.

OO Approach over Functional Approach

  • Everything can be represented in an object and it has the attributes associated with it and hence the state of the object is known at any time.
  • A complex system can be subdivided into modules of objects where as in functional approach, many functions inter operate to form a complex system.
  • Re-usability (i.e.) OO approach enables programmers to create new objects that inherits many of its features from existing objects. This makes object-oriented programs easier to modify than functional.
  • Debugging in Object Oriented environment is easy compared with functional approach where a small error in one function causes the whole system (i.e. all the functions) to fail.
  • Suitable for making application-oriented software.

Both the approaches have their advantages and disadvantages. What if we could get the best of both the worlds? The next section deals with the aspects and features of how well both the approaches are mixed together in some languages.

Mixing Functional & OO Code - Best of both the worlds

Languages that are a mix of functional and object oriented approaches(eg. Scala[1], Clojure[10], etc.) make use of best of both the worlds. They support various aspects of functional and object oriented approaches. They have a bunch of functional approach features[6], which object oriented programming languages(like Java) lack which includes closures. Closures are first-class functions with free variables that are bound in the lexical environment. These languages also sport a very malleable syntax that makes them well suited for "domain specific languages" with the benefits of static typing. They are more expressive, extensible and allow reuse of programming abstractions.

Certain languages like Scala, Clojure, etc run on Java Virtual Machine(JVM) ensuring portability to all underlying platforms. They even support immutable objects. In Scala, new language constructs can be added in the form of libraries. They allow safe reuse[9] of programming abstractions and type-safe extension of software.

Lambda

A lambda[5] expression is an anonymous function that contain expressions and statements, and can be used to create delegates or expression types. Such kind of anonymous function can be defined and passed around as easily as other data types and can be used to contain functionality that need not be named. Some notable examples include closures[5] and currying. Lambda functions with no name can be called using the variable assigned to it.

 g = lambda x: x*2  
 g(3)=6

We can have lambda function without assigning it to a variable which can be used as inline-function.

 (lambda x: x*2)(3)

In Clojure as a functional language, the creation of a chunk of behavior that can be assigned to a variable (known as a lambda) is trivial. Below are two ways to create a lambda that returns the square of its argument.

 (def square1 (fn [n] (* n n)))
 (def square2 #(* % %))

Currying

Currying transforms a function that takes multiple parameters into a chain of functions, each taking a single parameter. It reuses a partially applied function where the function is originally defined as accepting n parameters.It can be interpreted as the technique to decompose a function that takes n parameters into n function each of them taking 1 parameter. For example to say in algebraic terms,consider the function f which takes 3 parameters x,y,z and Currying means that we can rewrite the function as a composition of 3 functions:

 f(x,y,z) = 4*x+3*y+2*z
 f(x)(y)(z) = 2*z+(3*y+(4*x))

In Scala, currying takes advantage of Scala’s syntactic sugar. Below is a currying example in Scala. This function creates a multiplier function which takes two arguments and partially defines it by assigning a constant to one argument and assigning it to a variable .Later the function is called by assigning the second argument.

 def multiplier(i: Int)(factor: Int) = i * factor => method named multiplier which takes two parameters is defined 
 val byFive = multiplier(5) _                     => The method is redefined with accepting one parameter and store it in a variable.
 val byTen = multiplier(10) _
 
 scala> byFive(2)                                 => Method called by giving the second parameter.
 res4: Int = 10
 
 scala> byTen(2)
 res5: Int = 20

Less Verbosity

In Java each variable should have its type defined beforehand. Whereas in languages like Scala, it is more concise and easier to read than the equivalent Java.

For example, a Map can be created with a simple syntax in Scala,Clojure than in Java:

Java

Map<String, Integer> nMap = new HashMap<String, Integer>();
nMap.put("one", 1);
nMap.put("two", 2);
nMap.put("three", 3);

Scala

var nMap = Map("one" -> 1, "two" -> 2, "three" -> 3)



Clojure

(doto (new Java.util.HashMap)(.put “a” 1)(.put “b” 2))


 

Here, Scala compiler knows that nMap uses Strings as keys, and Integers as values. Unlike Java, this need not be specified beforehand. Scala could figure it out itself. Moreover, Scala will give an error if a different key-value pair is tried to insert. This is called "type inference". This prevents a whole class of bugs at run time just like Java but with less verbosity. In a similar way Clojure also involves only few lines of code compared with the object oriented Java language which takes many lines of code.A simple Clojure example and the associated Java code : Example

  (defn blank? [s] (every? #(Character/isWhitespace %) s))

The innermost function Character/isWhiteSpace checks the first argument % for any whitespace and returns true if it is. every? invoke the above inner function for all elements in the collection s.It has no variables, no branches etc.

Java code isBlank() method checks to see whether a string is blank or contains only whitespace.

 public class Stringcheck{
    public static boolean isBlank(String str){
          int len;
          if(str==null || (strlen =str.length())==0)
             return true;
          for(int i=0; i<strlen;i++) {
                if((Character.isWhitespace(str.charAt(i))==false)){
                       return false;
                   }
            }
        return true;
     }
 }

Pattern Matching

To say simply, a Pattern matching is a technique to search string for the occurrence of any patterns or regularity. Pattern matching is an elegant way to decompose objects into their constituent parts for processing.Many languages support the concept of pattern matching. Perl supports pattern matching using the pattern definition mini language called regular expressions, also called regexes or REs, which is borrowed from the regular expressions used in many Unix tools, such as grep() and sed().The risk that the parts of an object might be changed outside of the control of the enclosing object is avoided. For example, if there is a Person class that contains a list of addresses, it will not be an issue to expose that list to clients if the list is immutable. The users will not be able to change the list unexpectedly.Given below is a simple example for pattern matching used in Perl:

$mystring = "Hello world!";  // Perls way of assigning string to a variable.
if($mystring =~ m/World/) { print "Yes"; } // Checks the string stored in variable mystring has the string "World".
$mystring =~ s/world/mom/; // Replaces the string "world" stored in mystring to mom.

Another sample of regular expression in java :

String mystring="Hello World" ; 
Pattern str = Pattern.compile("\\w+");  => Pattern stored for matching.
Matcher fit = str.matcher(mystring);    => Matcher class which perform matching of the string with the pattern defined.
if(fit.matches())

As languages like Scala and Clojure support procedural code, pattern matching is done as equal or with less difficulty compared with Java which needs to use regex. Scala can even be embedded into XML as its syntax is somewhat similar.Given below an overview of how regular expression is achieved in Clojure. Data are manipulated in Clojure through the sequence called seq which is a logical list. Clojure can access java collections, its collections, Regular expression matches via the seq also called seq-able. Clojure's regular expression uses java.util.regex library at the lowest level. For achieving that the keyword re-seq is used.

Syntax:
  (re-seq regexp string)
Example
  (re-seq #"\w+" "Hello World") => ("Hello" "World")  => #"\w+" represents the java.util.regex.Pattern

Java Interoperability

Considering Clojure, it gives clean, simple and direct access to java and can access Java API directly.Clojure doesnot wrap Java's string functions. We can call using Java interop forms. It provides syntax for accessing Java elements like classes, instances, constructors, methods and fields and can also wrap Java API. The “.” dot special form is used for accessing Java. Some Clojure syntax which can access Java API:


Java Clojure
new Widget(“red”) (new Widget “red”)
Math.PI (.Math PI) or Math/PI
System.currentTimeMillis() (.System currentTimeMillis) or System/currentTimeMillis
rnd.nextInt() (.rnd nextInt) or (.nextInt rnd)


Clojure pass functions as arguments to other functions. But Java does not support this. So Clojure provides a member function memfn macro to wrap methods or can use anonymous function to wrap a method call.

   (map (memfn toUpperCase) [“a” ,”short”,”message”]) 

or

   (map #(.toUpperCase %)[“a”,”short”,”message”]) => #(body) represents the anonymous function in Clojure.

For example,

   (def the-digits
   (map #(Integer. (str %))(filter #(Character/isDigit %) (seq big-num-str))))

    where (def big-num-str (str "123785334434se9088af8309304293872adbcdfd”))

The above code extracts the integers from string and discards the non-numeric characters. Lets look at the functions defined in the example. There are totally five functions defined.They are

    (seq big-num-str)          => get each character defined in big-num-str
    (Character/isDigit %)      => Checks whether the input is an integer
    (filter #())               => Filter each string from str
    (map #(Integer. (str %)))  => Change the string to integer 
    (def the-digits)           => Output the integers only in a string of characters.     

Here,

   #(Character/isDigit) represents the Java method Character.isDigit() and #(Integer.(str %)) represents the new java.lang.Integer(str(%))

Most of the Clojure library functions have defined semantics for objects of Java types. contains? and .get work on Java’s Maps, arrays, Strings, count works on Java Strings, Collections and arrays. Clojure is built around these aspects. It avails the performance of JVM and the richness of both the core APIs and the numerous third-party libraries written in the Java language and restrained from reinventing them.

Multiple Inheritance

Multiple Inheritance2 is the concept of using the methods and variables of more than one superclass to a sub class.Java was designed without multiple inheritance. But Java supports the solution of problems commonly solved with multiple inheritance in other ways with help of interfaces where the methods defined in it are not implemented.In Scala, classes are extended by subclassing and a flexible mixin-based composition mechanism as a clean replacement for multiple inheritance in Java.Lets see how multiple inheritance achieved in Scala:

As interfaces in Java, Scala allows traits but has some methods partially implemented. The only difference to classes is the traits may not have constructor parameters.

 trait Similarity {
 def isSimilar(x: Any): Boolean   // not implemented
 def isNotSimilar(x: Any): Boolean = !isSimilar(x) //implemented method
 }

This trait defines two methods isSimilar and isNotSimilar where isSimilar does not provide a concrete method implementation (it is abstract in the terminology of Java), and the method isNotSimilar defines a concrete implementation. Consequently, classes that integrate this trait only have to provide a concrete implementation for isSimilar.

 class Point(xc: Int, yc: Int) extends Similarity {
   var x: Int = xc
   var y: Int = yc
   def isSimilar(obj: Any) =  //need to implement the method here 
   obj.isInstanceOf[Point] &&
   obj.asInstanceOf[Point].x == x
  }
 object TraitsTest extends Application {
   val p1 = new Point(2, 3)
   val p2 = new Point(2, 4)
   val p3 = new Point(3, 3)
   println(p1.isNotSimilar(p2))
   println(p1.isNotSimilar(p3))  //uses the already defined method 
   println(p1.isNotSimilar(2))
  }

These are some of the aspects and traits of mixing functional and object-oriented approaches. As we discussed in the advantages section, both the approaches have their benefits and limitations. Mixing them aspect-wise enables the above features in such languages. Lets discuss those languages briefly in the next section.

Languages Supporting Functional and OO code

OCaml and F#[7] are some of the languages which support both functional and object oriented programming. OCaml is as fast as C and its featured as Haskell which is a pure functional language. F#[3] is a dynamically typed language which can run on .NET framework and has supported immutable types i.e. tuples, records, discriminated unions and lists to work in Functional programming. It has functions that can either be in curried or in uncurried form. As in functional language it can pass functions as arguments to other functions, resulting in higher order functions. It also supports lambda functions and closures. F#,behaves like other .NET languages as both in imperative and object oriented style.

Clojure[10] is also a functional programming language and it is a dialect of Lisp. It is not a pure functional language and it is a dynamically typed language. Just like Java it runs on JVM platform. But its syntax differs from Java. It is represented as :

 (functions arguments...)

The function followed by its arguments is enclosed in paranthesis. It has features like immutable data structures, high-order functions and recursion, easy and fast java interoperability.

Python and Ruby are also the programming languages which offer the mix of functional and OO languages. But these languages doesn't support the algebraic data types and pattern matching.

Conclusion

The above aspects of functional and object oriented programming techniques discuss some if their merits and demerits. Mixing them produces a new programming paradigm which leads to a new dimension in programming. A few fundamental concepts are explored in Clojure and Scala here. The ability of Clojure and Scala to run in JVM platform makes them more preferable than others in the market. Some of the merits like closure, less verbosity, high order functions, currying along with interoperation with previously written libraries in these languages make them more efficient and useful. There are also other languages which support functional and object oriented techniques. Mixing the approaches, evolve a new dimension in programming which is practical and opens doors to more exploration.

References

  1. Bagwell. Learning Scala. Available from http://www.scala-lang.org/node/1305 (2009); Accessed 24 April 2010.
  2. Bjarne Stroustrup. Multiple Inheritance for C++. 1999.
  3. Chris Smith. Programming F#. O'Reilly Media, Sebastopol, CA, 2009.
  4. Jean E. Sammet, David Hemmendinger. Encyclopedia of Computer Science. John Wiley and Sons Ltd., Chicester, UK, 2003.
  5. Jeremiah Willcock, Jaakko Jarvi, Doug Gregor, Bjarne Stroustrup, Andrew Lumsdaine. Lambda expressions and closures for C++. 2006.
  6. Lee Braine. An Object-Oriented Functional Approach to Information Systems Engineering, Department of Computer Science, University College London.
  7. Microsoft Corporation. Visual F#. Available from http://msdn.microsoft.com/en-us/library/dd233154.aspx (2010)
  8. Ph. Narbel. Functional Programming at Work in Object-Oriented Programming. ETH Zurich, Chair of Software Engineering, Journal of Object Technology, 2009. Vol. 8, No. 6.
  9. Shriram Krishnamurthi, Matthias Felleisen, Daniel P. Friedman. Synthesizing Object-Oriented and Functional Design to Promote Re-use. European Conference on Object-Oriented Programming, 1998.
  10. Stuart Hallway. Programming Clojure. Pragmatic Bookshelf, USA, 2009.