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 Animal
Dog
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.