DEV Community

tjray-dev
tjray-dev

Posted on

OOP & The func(y) => Bunch

OOP & The func(y) => Bunch

Part, The First: A Tale of Two Paradigms

Disclaimer 1: I am not and do not claim to be an expert on any of the topics you are about to read about. Do not consider this a comprehensive guide, instead look at this as the fledgling opinions of a fledgling developer.

Now, with that out of the way.

A Series of Unfortunate Questions

Disclaimer 2: I will be looking at these questions and the broader topics through the lens of JavaScript

  • What is "Clean" code?

It was this question that first sparked this journey into the realm of coding paradigms and their kaleidoscopic facets and it is this question that we will keep coming back to.

A simple google search of that question yields ~ 2 billion (1,940,000,000 to be exact) search results. So clearly I'm not the only person to think on this, theres even an aptly named book on the topic Clean Code. However I came across a set of acronymns that give us a good starting point.

  1. KISS: (Keep It Simple Stupid) The philosophy of rendering down a design to its least complicated form. Ask yourself: 'Can this be written in a simpler way?'

  2. DRY: (Don't Repeat Yourself) The Mantra of Senior Developers and Coding Instructors every where. Ask yourself: 'Can this be abstracted and reused?'

  3. YAGNI: (You Ain't Gunna Need It) This one is a bit more for larger scale projects than what technomancers in training (like myself) might come across. Ask yourself: 'Does this functionality bring me closer to my MVP?'

With these we have a few guide posts when writing our code, unfortunetly, we now have a few more questions. So keep those in the back of your mind, because. . .

Alt Text

So Long and Thanks for All The Paradigms

"Mr/Ms blog writer person," I hear you ask, "What is a Paradigm exactly?"

Well I'm not going to get into the finer points of it but for our purposes I'll be using this very simple definition:

A Paradigm is a collection of beliefs and thought patterns, a guiding philosophy if you will

There a Many Coding Paradigms, in this context we will only be going over two; Functional Programming and Object Oriented Programming.

The F Word (Functional, the word is Functional)

From the Wiki page

In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions. . .

And with that we now have two new questions; first, what is a function? Second, what is an expression

A Simple Example
// in the math.js file
const add = (a, b) => a + b

in the example the line as a whole is a function (never mind that this is technically an arrow function expression ...because naming is hard) while everything to the right of the arrow (=>) is an expression. This also fits nicely into a few of the core tenets of Functional Programming. But first a quote:

Functional Programming imposes discipline on mutating state

The first principle of functional programming we are going to look at is the idea of 'Pure' functions. A pure function is a one that is, predictable, does not mutate any data that it takes in, and that only returns a single value; in our example above our function is pure. The purity of a function is paramount* in writing functional code, so much so that all of the other principles we will be covering rely on or intersect with it.

Pure functions and their predictability allow for a certain level of transparency in the code, for example:

A Simple Example v2

    // in the math.js file
    const add = (a, b) => a + b
    const add = (1, 2) => 1 + 2
Enter fullscreen mode Exit fullscreen mode

In our modified example the values we are passing into the function and the expression they form are clear, and a single result is returned from the expression resolving and as such is side effect free.

The second principle of functional programming that we are going to look at is the concept of functions as first-class entities. What this means in general is that functions can be treated as data, meaning they can be passed around the same as variables. This mean that functions can be declared as variables, take another look at our examples, what we are actually doing here is assigning an anonymous function to the const variable 'add'. It is important to note that this particular syntax is a built in part of javascript, this will become more important in a bit, but for now lets look at a few more examples of pure functions acting as basic first-class entities.

A Simple Example v3

    // in the math.js file
    const add      = (a, b) => a + b
    const subtract = (a, b) => a - b
    const multiply = (a, b) => a * b
    const divide   = (a, b) => a / b
Enter fullscreen mode Exit fullscreen mode

Don't they just look so wonderful and 'clean'. . .

The third and final principle we will be covering is that of higher-order functions. Higher-order functions allow for functions to return other functions, as well as take in first-class functions as arguments. This is where the magic starts to happen with Functional Programming, check out this example:

A Simple Example v4

    // in the math.js file
    const add      = (a, b) => a + b
    const subtract = (a, b) => a - b
    const multiply = (a, b) => a * b
    const divide   = (a, b) => a / b    
    const equation = (a, b) => subtract(multiply(add(a, b), divide(a, b)), add(multiply(add(a,b), divide(a, b))))
    equation(add(1, 2), subtract(5, 4))
Enter fullscreen mode Exit fullscreen mode

Well reader? Is this pure? Is this perhaps even 'clean'?

With these principles in mind a pattern begins to emerge, but lets pause here to address a core principle of functional programming, namely the concept of Immutablity, a concept that can be easy to express and talk about in theory, but complicated to implement.

In functional programming data once assigned is treated the same as functions and as such should never be altered once set, this shores up our pure functions by allowing our base data to be predictable. There are drawbacks though, specifically concerns around space-time (space-time here meaning the amount of physical space something takes up in memory as well as the time needed to perform an action on that thing). Now, I hear you asking: "But Mr/Ms blog writer wouldn't copying a large data set in its entirety get very space-time intensive very quickly?" The short answer is yes. The longer answer is also yes. In fact the only answer is yes. The good news it that Languages built on functional principles have built in ways of dealing with this, the bad news, for us, is that JavaScript isn't one of those languages. There are answers to this for JavaScript devs in the form of various libraries, none of which I will be covering as they fall outside the scope of this particular discussion, but for those intrepid readers eager to learn more on the topic this video should be a good place to start.

Now that we have a base understanding of functional programming lets move one to the other paradigm we will be looking at The Notorious O.O.P

The Tell Tale Object

From the wiki

Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).

In Object Orientation the idea of the 'Object' reigns supreme (I know, shocking right?) but what do we mean when we talk about 'Objects', well, an object at its core is simply a data structure that is able to house data(values) and methods(functions). However this data structure is capabale of something special, but more on that later, first let's unpack this relationship between Objects, data, and methods. So lets talk about Encapsulation.

Encapsulation is the principle of scoping data(values), and methods(functions) to a particular object, often(but not always) defined as a 'class'. While this may or may not be THE core priniciple of OOP it will be essential later on, so add it to the pile of things to keep in mind as you read on. By encapsulating our code in classes we are able to seperate out the concerns of each object, meaning that data(values) and methods(functions) are grouped together by their relevence to each other. Lets revisit our example, (I will be switching to ruby for these examples, but the concepts are more important than the particular syntax)

A Simple Example v5

    # in the math.rb file
    class Math

      def initialize
        num_a = 1
        num_b = 2
        num_c = 3
        num_d = 4
        num_e = 5
        num_f = 6
      end

      def add(a, b)
        a + b
      end

      def subtract(a, b)
        a - b
      end

      def multiply(a, b)
        a * b
      end

      def divide(a, b)
        a / b
      end

      add(num_a, num_b)
      subtract(num_a, num_b)
      multiply(num_a, num_b)
      divide(num_a, num_b)
    end
Enter fullscreen mode Exit fullscreen mode

Here we see encapsulation in action. This class of math is fully self contained. We have a Math class(object) containing all of the Math methods(functions) as well as all the data(values) that are needed to perform the appropriate operations, very clean, much organized, such Object, etc. As a quick aside, classes are often(but not always) given their own file in the code base, this is a convention that can be applied or ignored as needed (as with most, if not all conventions in coding). It is important that as written this code can only be executed inside of itself, no other code within the larger (purely hypothetical) code base we are working with, so if we had multiple classes within a single file they would each be encapsulated with there own scope. So, is this code 'clean'?

Of course if we have code that closed off from the rest of our code base, that obviously makes having complex programs that do more than one thing a bit tricky. This is where our second principle comes into play, the concept of Inheritance. With Inheritance we can designate a parent and child relationship between two classes allowing the child to utilize and of the data(values) and methods(functions) of the parent. But lets get a little more abstract with our example

A Simple(?) Example v6

    # in the math.rb file
    class Math < Numbers

      def add(a, b)
        a + b
      end

      def subtract(a, b)
        a - b
      end

      def multiply(a, b)
        a * b
      end

      def divide(a, b)
        a / b
      end

      add(@num_a, @num_b)
      subtract(@num_a, @num_b)
      multiply(@num_a, @num_b)
      divide(@num_a, @num_b)
    end

    class Numbers

       def initialize
        @num_a = 1
        @num_b = 2
        @num_c = 3
        @num_d = 4
        @num_e = 5
        @num_f = 6
      end

    end
Enter fullscreen mode Exit fullscreen mode

In the example here, the Math class(object) is inheriting the values of @num_a, @num_b, etc. with the use of inheritance we are able to separate our program into larger concerns beyond itself allowing for classes(objects) to remain focused on their particular concern while still having access to and operating on code outside of their own scope. With this our program is capable of increased depth of functionality while maintaining 'cleanliness'.

By utilizing these concepts we are able to use metaprogramming and abstract out our code(sorry no particular example for this one) in an effort to make our programs as light weight as possible without sacrificing depth of functionality or readability.

*Insert sick neat meme here *

Epilog: The Good, The Bad, The Stylistic

In my research for this blog it occured to me that these two paradigms are not really all that different, the core difference in my eyes is the scope of their concerns, this is best summed up by the following; functional programming does not have a concept for things like class(object) at all. The closes functional programming comes to this is with state, which for our purposes is the equivalent of an OO global variable. What does all this mean for us? It means that these two paradigms can be mixed, the extent to which this can be done is determined by the features of the language being used, lucky for us JavaScript exists.

By writing methods(functions) with an eye towards purity we are able to create complex procedures that produce complex outputs that are clean and predictable,

FURTHER READING

SOURCES

Discussion (0)