This site lists the protips that we shared with students during our courses
Programming languages come in many different flavors, just as human languages. Each language gives you different opportunities and possibilities to express a solution to a problem. Again just as human languages - let’s examine that a bit deeper.
For example, consider Chinese - a language that is made up of signs. There’s a sign for house and another for wagon. If you want to express the concept of a mobile home - you will make a new sign that is the combination of house and wagon (I presume - I don’t know Chinese.)
In the language of heroes and Gods (Swedish) we instead use letters to build words. The word for “house” is h, u and s put next to each other, with spaces before and after - to make the word hus. Wagon is vagn. If you want to represent the concept of mobile home you would combine the letters into a new word husvagn.
The type of language decides how we describe things and how we think about things.
There are a few different types of programming languages that, in a similar fashion as human languages, affects how we think about the task at hand. This post will describe two of the two big types of programming languages, but there are many more types.
You can’t decisively say that one type of language is better than another language; they are all good in the things they are designed to do. You can solve just about any problem using any kind of language (at least the general purpose programming languages) but they all have their strengths and weaknesses.
Before we start; the topics that we describe here are both 10-week courses at university and then took me (Marcus) at least 2-3 years more to use to my advantage in real life. This will be a very shallow brush on the surface.
Object oriented languages (OOP - object orient programming) is obviously based around the concept of objects. These objects contain data and operations to work on that data.
Famous OOP languages are C#, Java, and Smalltalk. OOP was made popular in the 80-is with the rise of the graphical user interface, that has a good fit for object orientation.
When writing OOP programs we represent our code as classes. Classes are templates for creating objects. Classes describe how the objects should be structured and behave. We describe the problem we are working with as structures of these classes. Classes can inherit functionality from other classes.
An (overused) example hopefully clarifies what this means. Let’s say that we are building a register for animals:
class Animal {
constructor(name) { this.name = name }
getName() { return this.name.toUpperCase() }
}
class Dog extends Animal {
constructor(name) { super(name) }
makeYourSound() { return `${this.getName()} says WOOOF` }
paws() { return 4 }
}
class Bird extends Animal {
constructor(name) { super(name) }
makeYourSound() { return `${this.getName()} says Tweet-tweet` }
beekLength() { return 2.5 }
}
Let’s go through this example and see a lot of the OOP paradigms in action. These are the different way that we think about a problem and we will see constraints and opportunities.
Animal that will hold all the information that is common for all animals, for example, a name.
constructor that is used to create new objects of this class. Notice that we need to supply a name to this, ensuring that all animals will have a name.getName. This is called encapsulation meaning that we hide the internal representation and only allow users of this class to get names through this method. Hence it will always be uppercasedDog, a specialization of animals. This will hold all the information about Dogs.
Dog class extends the Animal class, meaning that inherits all the data and methods from Animal.
Dog has a name, that is defined on AnimalDog we will use the constructor, passing the name as before. But we will call the super(name) method and pass its name. This is how we call Animals constructor to set up the animal in the correct way.paws method that returns the number of pawsmakeYourSound method will return a string that represents how the Dog sound. Notice that Dogs always says Woof. An abstract Animal don’t have a makeYourSound method since we don’t know how those (abstract animals) sound. It doesn’t make sense talking about sounds for animals, without knowing what type of animal it is.Animal called Bird.Animal but has a specialization implementation of makeYourSound that makes it sound like a bird.beekLength method that returns the length of the beek, which will be the same for all birds (that is strange but I ran out of imagination)Let’s use these classes and create a few concrete objects:
const d = new Dog('Fido')
console.log(d.makeYourSound()) // Logs: FIDO says WOOOF
console.log(d.paws()) // 4
const b = new Bird('Jacko')
console.log(b.makeYourSound()) // JACKO says Tweet-tweet
console.log(b.beekLength()) // 2.5
We create an instance of the Dog class by using the new keyword, that will call the constructor passing Fido as the name of the Dog. The instance is called an object and we store it in a variable called d. There’s only one and has the name Fido. We can see that by calling the makeYourSound()` method.
The same behavior can be witnessed as we create a Bird object const b = new Bird('Jacko'). The makeYourSound method now returns the sound of the bird.
Now, we know that everything inheriting from Anmial have a getName method so we can use that to our advantage. Let’s create an array of Animals:
const d = new Dog('Fido')
const b = new Bird('Jacko')
const animals = [d, b]
animals.forEach((a) => console.log(a.getName()))
// FIDO
// JACKO
Both d and b is Animals since they extends animals and we can safely use getName to logs their individual names.
But even cooler is that we can use the makeYourSound method on both of the animals. They both have them and we don’t need to care what kind (or type) of animal it is - as long as we know that it has a makeYourSound method.
animals.forEach((a) => console.log(a.makeYourSound()))
// FIDO says WOOOF
// JACKO says Tweet-tweet
Now it a behaves differently depending on what type it is, but our code doesn’t care. This is called Polymorphism.
(Notice that since JavaScript (that the code is written in) doesn’t have basic OOP features has overrides and interfaces the example is not complete, but showing a point.)
Functional programming (FP) is oriented around functions that you pass data too. We strive to write immutable (once defined, unchangeable) data and pure (stateless) functions. Program is made up by composing many small functions into a larger whole.
FP is much older than OOP and dates back to the childhood of computer science (1950-is). Commonly used functional programming languages are JavaScript, Haskell and Erlang (or Elixir).
In FP our function operates on data (objects) and we seldom describe the template (classes) for such data. Function themselves are first class citizens and very often is passed as parameters to other functions, building so-called high-order functions (functions that take functions as parameters).
Again, an example might be useful:
const dogPrinter = d => `${d.name} has ${d.paws} paws and says WOOF`
const birdPrinter = b => `${b.name} says tweet-tweet with her ${b.beekLength} cm beek`
const animalPrinter = a => {
switch (a.type) {
case 'Dog':
return dogPrinter(a)
case 'Bird':
return birdPrinter(a)
}
}
const animalSorter = (a, b) =>
a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
(Yes, I’m using the arrow function syntax throughout my example - read up on it if it confuses you. Also, I’m not a fan of semi-colons where they are not needed. So I have not used them - sue me! :))
Let’s go through the code line by line
dogPrinter that returns a string representing a dog.
.name .paws. We will also always return ‘WOOF’. Since this is the dog printer we can do thatbirdPrinter that does similar things for birds..type and then calls the correct print function. If you pass animalPrinter something that doesn’t have a .type property or if the a passed to the function not is Dog or Bird it will fail.animalSorter that returns 1 if a.name is greater than b.name. This will be used to sort and tell us which of the animals (a or b ) that comes before another in an alphabetically sorted list.
animalSorter needs data passed to it with .name-properties. It doesn’t care if it is animals or not. Hence it could be called … name sorter or something like that.Let’s use the functions and put them together. First, we need some data to operate on:
const animalList = [
{ name: 'Jacko', type: 'Bird', beekLength: 2.5 },
{ name: 'Fido', type: 'Dog', paws: 4 }
]
animalList
.sort((a, b) => animalSorter(a, b))
.map(a => animalPrinter(a))
.forEach(el => console.log(el))
animalList is an array of animals. Notice that they are not formatted the same, but have some common properties, such as name and type
animalList
animalSorter for each element.
.sort takes two parameters, two elements from the animalList and just call into the animalSorter.sort will go through the entire array and call animalSorter for all the elements and create a new sorted array, according to the animalSorter rule.sort will return a new sorted array.map.
.map takes an array and calls the function for each element to produce another, new array
.sort is actually a special version of .map (.map is a generic version calling a function for each element in an array, returning a new array)animalPrinter function for each animal.animalPrinter will return a string for each, meaning that .map will return an array of stringsconsole.log, using the .forEach function
.forEach function that just executes a function per element but doesn’t return anything.There’s an even tighter way to call the functions above, that you might see. If all there parameters that a function takes are the same as the parameter of the calling function we can just write the name of the function.
So this part:
animalList
.sort((a, b) => animalSorter(a, b))
.map(a => animalPrinter(a))
.forEach(el => console.log(el))
Could be written like this:
animalList
.sort(animalSorter)
.map(animalPrinter)
.forEach(el => console.log(el))
Notice some of the functional paradigms in action here:
From Wikipedia we can read:
JavaScript has curly-bracket syntax, dynamic typing, prototype-based object-orientation, and first-class functions.
and
As a multi-paradigm language, JavaScript supports event-driven, functional, and imperative (including object-oriented and prototype-based) programming styles.
This means …
JavaScript is not really OOP but rather more FP. Meaning you can do most of the things needed to call it an FP-language but not all of it (pattern matching is lacking for example).
You can do some of the OOP things too but it has an even poorer fit.
Thinking in FP paradigms and patterns is most useful when doing JavaScript development.
Object-oriented programming (OOP) is built around the object that encapsulates data and methods that operate and change that data. We represent the problem we are working with as classes (templates for objects) that inherit functionality from each other.
Functional programming (FP) is built around functions that operate on data. Functions are often passed as parameters to other functions (high-order functions) making up more advanced features from simpler. Functions should be stateless and operate on data passed to it (pure function). The functions should not modify the data passed to it but rather return new data (immutable).
I hope it’s a bit more clear what the difference between the two paradigms are. If not, here’s a lovely graphics that probably is a much better summary than I can do. It is from https://www.educba.com/functional-programming-vs-oop/ and is really good.
