Functional vs. Imperative Programming
The 2 biggest programming paradigms are functional and imperative, but they share very little in common. In this blog post, I will discuss the differences between the two as I understand them and talk about my experience with each. Functional programming languages are very mathematical - functions can be defined piece-wise, recursively, or in terms of other functions. They can also easily be combined forming composition of functions. Imperative languages use a slightly different approach - rather than telling the computer what something is, you must specify how to get it.
The first functional programming language I learned was Racket, which is a similar to another functional programming language: Common Lisp. I always thought of Racket as a neat language with some nice abstractions but I always felt like it lacked some core functionality. Recently, however, my friend introduced me to Haskell, and I've been trying to pick it up. Haskell is much more powerful than Racket, and it's one of the most widely used functional programming languages. I am amazed by the level of abstraction in Haskell and I have barely even scratched the surface! Java was the first imperative language that I learned, but I have also used C, C++ and some others. Imperative languages are useful in a lot of situations and much more widely used than functional languages, but they lack a lot of the abstractions that come to be expected in functional programming languages. Here are a few key points that I will talk about in this post.
Mutability is the first and biggest difference that comes to mind when comparing functional and imperative programming languages. Objects in imperative languages, such as Java, are Mutable by default, but objects in (most) functional languages are Immutable by default. In imperative languages, when you define an object, you are allowed to mutate it's fields after it's been created. In functional languages that's not possible. In a functional language, if you wanted to change the fields of a certain object, you would have to create a new object and copy the values over from the original object. While that seems like a lot of work, and it is, it also has a lot of nice properties. For instance, the compiler knows for certain that the state of an object will never change, so it makes assumptions that allow it to optimize the source code. Additionally, imperative programmers sometimes spend a lot of time debugging problems related to mutation. On the other hand, mutating an object is faster than creating a new copy, so when performance is critical, mutation is an import feature to have.
Abstraction is a key concept in computer science, and programmers should always seek to abstract solutions to problems so they can be reused at later times. Both functional and imperative languages support abstraction, but the level of abstraction in functional programming far exceeds that of it's counterpart. Java offers abstraction in the form of interfaces, super-classes, using the idea of polymorphism to implement shared behavior. Haskell implements abstraction in many different ways, and I haven't even learned them all yet. Just like Java, you can define functions that work on an abstract type (but it's much more elegant) by simply specifying type signature and writing the function. Haskell also supports infinite data structures, pattern matching, function composition and much more! In my admittedly limited experience, it's much more natural to write reusable code in Haskell than it is in Java.
When it comes to efficiency, imperative languages have the upper hand, because functional languages are built on so many abstractions, they weren't specifically optimized for a particular use case. In certain cases, Java or C would be preferable for these performance reasons, but in general the features offered by Haskell (and other functional languages) more than overcompensate for the efficiency losses in my opinion, although it depends on the type of problem you are working on.
Although I'm relatively new to Haskell, I definitely see and appreciate the upside that it has to offer, and I recommend every Computer Science student to at least try it. Learn You a Haskell is a great place to get started, as it contains a very thorough walk-through of Haskell.
The first functional programming language I learned was Racket, which is a similar to another functional programming language: Common Lisp. I always thought of Racket as a neat language with some nice abstractions but I always felt like it lacked some core functionality. Recently, however, my friend introduced me to Haskell, and I've been trying to pick it up. Haskell is much more powerful than Racket, and it's one of the most widely used functional programming languages. I am amazed by the level of abstraction in Haskell and I have barely even scratched the surface! Java was the first imperative language that I learned, but I have also used C, C++ and some others. Imperative languages are useful in a lot of situations and much more widely used than functional languages, but they lack a lot of the abstractions that come to be expected in functional programming languages. Here are a few key points that I will talk about in this post.
- Mutability
- Abstraction
- Efficiency
Mutability is the first and biggest difference that comes to mind when comparing functional and imperative programming languages. Objects in imperative languages, such as Java, are Mutable by default, but objects in (most) functional languages are Immutable by default. In imperative languages, when you define an object, you are allowed to mutate it's fields after it's been created. In functional languages that's not possible. In a functional language, if you wanted to change the fields of a certain object, you would have to create a new object and copy the values over from the original object. While that seems like a lot of work, and it is, it also has a lot of nice properties. For instance, the compiler knows for certain that the state of an object will never change, so it makes assumptions that allow it to optimize the source code. Additionally, imperative programmers sometimes spend a lot of time debugging problems related to mutation. On the other hand, mutating an object is faster than creating a new copy, so when performance is critical, mutation is an import feature to have.
Abstraction is a key concept in computer science, and programmers should always seek to abstract solutions to problems so they can be reused at later times. Both functional and imperative languages support abstraction, but the level of abstraction in functional programming far exceeds that of it's counterpart. Java offers abstraction in the form of interfaces, super-classes, using the idea of polymorphism to implement shared behavior. Haskell implements abstraction in many different ways, and I haven't even learned them all yet. Just like Java, you can define functions that work on an abstract type (but it's much more elegant) by simply specifying type signature and writing the function. Haskell also supports infinite data structures, pattern matching, function composition and much more! In my admittedly limited experience, it's much more natural to write reusable code in Haskell than it is in Java.
When it comes to efficiency, imperative languages have the upper hand, because functional languages are built on so many abstractions, they weren't specifically optimized for a particular use case. In certain cases, Java or C would be preferable for these performance reasons, but in general the features offered by Haskell (and other functional languages) more than overcompensate for the efficiency losses in my opinion, although it depends on the type of problem you are working on.
Although I'm relatively new to Haskell, I definitely see and appreciate the upside that it has to offer, and I recommend every Computer Science student to at least try it. Learn You a Haskell is a great place to get started, as it contains a very thorough walk-through of Haskell.
Comments
Post a Comment