In this article, we will analyze JavaScript and PHP languages and their support for functional programming paradigm.
However, to do so, we will first analyze if those languages are Object-Oriented, and if they are, how can we be
certain of that?

Through analysis of their support for the Object-Oriented paradigm, we will establish a systematic approach and critical
thinking in the evaluation of the properties of one language to be able to classify it in a certain paradigm. We are
starting with the Object-Oriented paradigm because there is no (too much) controversy when it comes to PHP, JavaScript
and Object-Oriented paradigm.

Support for Object-Oriented paradigm

JavaScript and PHP are similar in that they do not have a compilation phase in design time, rather they are interpreted
languages. Nowadays, they do have a compilation phase in runtime for the purpose of execution optimization, but that is
not important for this article.

In order for some language to be considered Object-Oriented, it must support certain features. Those features are:
inheritance, encapsulation, polymorphism and abstraction.

If JavaScript and PHP support those features, we can safely say they are Object-Oriented languages. So, let's see if
they do.

Inheritance

Inheritance is a mechanism in which a new class is derived from an existing
class [1]. In both JavaScript and PHP, we achieve
inheritance by using the extends keyword, so here are some code snippets that demonstrate inheritance in both of
the languages.


// JavaScript
class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name} makes a noise.`);
    }
}

class Dog extends Animal {
    speak() {
        console.log(`${this.name} barks.`);
    }
}

// PHP
class Animal {
    public $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function speak() {
        echo "{$this->name} makes a noise.";
    }
}

class Dog extends Animal {
    public function speak() {
        echo "{$this->name} barks.";
    }
}

One could argue that JavaScript does not support class-based inheritance, but rather it supports prototypal
inheritance[2]. However, class-based inheritance is not a
requirement for inheritance. Both class-based and prototypal inheritances are valid ways to achieve it.

It is interesting that Go language does not have inheritance, but it is still considered an
Object-Oriented language (well, kind of) [3]. Considering that
"composition over inheritance" is a common practice in modern software development, it is no wonder why some of the
experts in the field are challenging the importance and requirement of inheritance in
OOP [4].

However, without any doubt, both JavaScript and PHP support inheritance.

Encapsulation

Encapsulation is a property of OOP that essentially prevents external code from being concerned with the internal
workings of an object[5]. Simply put, it is a way to
hide properties/methods from the outside world. In JavaScript, we have private and public keywords to achieve
encapsulation, while in PHP we have private, protected and public. We call them access modifiers ("member
visibility" is used as a term as well, and there are a few more terms used out there in the wild).


// JavaScript
class Animal {
    #privateProperty = 'private';
    publicProperty = 'public';

    #privateMethod() {
        /../
    }

    publicMethod() {
        /../
    }
}

// PHP
class Animal {
    private $privateProperty = 'private';
    protected $protectedProperty = 'protected';
    public $publicProperty = 'public';

    private function privateMethod() { /../ }
    protected function protectedMethod() { /../ }
    public function publicMethod() { /../ }
}

One could argue that JavaScript does not have protected keyword, but it is not important for the "encapsulation" box
to be ticked because encapsulation does not require access modifiers, those are just implementation details of the
languages. Encapsulation only requires the possibility to hide "internal workings of an object", which both languages
provide.

Therefore, it is obvious that both JavaScript and PHP support encapsulation.

NOTE: Prior to the class syntax in JavaScript, we used the constructor function to achieve
encapsulation[6], and even that was considered as valid encapsulation.
Moreover, no one has any doubt that Python is an Object-Oriented language, even though it does not have access modifiers
at all and encapsulation is achieved in userland with notation (by using an underscore prefix).

Polymorphism

Polymorphism requires the possibility to represent multiple types with a single
symbol[7]. This definition is a bit vague, as polymorphism
is quite a complex topic. The most common forms of polymorphism are ad-hoc
polymorphism[8], parametric
polymorphism[9], and subtype
polymorphism[10].

Going into detail about each form of polymorphism is out of the scope of this article, but if you are interested in a
topic, I encourage you to read more about it starting from provided
references[7][8][9][10].

Needless to say, both JavaScript and PHP support polymorphism, as they are dynamically typed languages. In general, all
dynamically typed languages are polymorphic by their nature. Previously stated can be easily fact-checked by going
through provided references, which I encourage you to do.

Abstraction

During my university studies, I was taught that Object-Oriented Programming requires inheritance, encapsulation, and
polymorphism. We called that "Holy threesome of OOP" (sounds dirty, I know). No abstraction was mentioned.

Simplified, abstraction refers to the ability to hide complex implementation details and show only the necessary
features of an object[11]. Intuitively, one can easily
conclude that abstraction in Object-Oriented programming is a direct consequence of encapsulation, polymorphism, and
inheritance (which can be a reason why during my studies we had "Holy threesome", not "Holy foursome").

Commonly, abstraction in Object-Oriented programming language is achieved by using interfaces and abstract classes.

PHP supports interfaces and abstract classes, so there is no doubt that PHP supports abstraction.


// PHP
interface Animal {
    public function speak();
}

class Dog implements Animal {
    public function speak() {
        echo 'Dog barks.';
    }
}

// PHP
abstract class Animal {
    public abstract function speak();
}

class Dog extends Animal {
    public function speak() {
        echo 'Dog barks.';
    }
}

Abstraction in typical class-based Object-Oriented languages depends on subtype
polymorphism[10], of course. Because of that, we can write code that depends
on general types (abstractions, contracts), not on concrete implementations. Hence, the core foundation of "Dependency
Inversion Principle"[12] from SOLID principles in
Object-Oriented programming[13] is an abstraction - "Depend upon abstractions,
not concrete implementations".


// PHP
class Shelter {
    private $animals = [];

    public function addAnimal(Animal $animal) {
        $this->animals[] = $animal;
    }
}

$shelter = new Shelter();
$dog = new Dog();

$shelter->addAnimal($dog);

JavaScript, however, does not support interfaces and abstract classes and one could easily argue that JavaScript does
not support abstraction. But that is not entirely true, it can be achieved in userland.

There is always a naive approach called "duck typing" (If it walks like a duck, and it quacks like a duck, then it must
be a duck
)[14]. In general, it means that an object instance can be checked
for certain properties/methods and if it has them, it can be considered as an "instance" of a certain "interface" or "
abstract class". However, this is just a bad idea since it is very error-prone.


// JavaScript
class Parrot {
    fly() {
        console.log('Fly my bird, fly!.');
    }
}

class Airplane {
    fly() {
        console.log('Engine started!');
    }
}

for (const something of [new Parrot(), new Airplane()]) {
    if (typeof something.fly === 'function') {
        console.log('It is a bird!'); // Not true for airplane
    }
}

JavaScript supports the instanceof operator, which can be used to check if an object is an instance of a certain
class. Well, not exactly an instance of the class, it will check if the given class constructor/function is in the
prototype chain of the given
object[15]. So all we need to do is
to mimic interfaces and abstract classes in JavaScript.


// JavaScript
/**
 * @interface
 */
class Animal {
    constructor() {
        throw new Error('Interface "Animal" cannot be instantiated.');
    }

    speak() {
        throw new Error('Method of interface "Animal" must be implemented.');
    }
}

/**
 * @abstract
 */
class Car {

    constructor(hasGasoline) {
        this.#hasGasoline = hasGasoline;
    }

    start() {
        if (!this.#hasGasoline) {
            throw new Error('Car has no gasoline.');
        }

        console.log('Engine started.');
    }

    break() {
        throw new Error('Method of interface "Animal" must be implemented.');
    }
}

class Parrot extends Animal {
    constructor() {
        super.constructor();
    }

    speak() {
        console.log('Parrot speaks.');
    }
}

class Lambo extends Car {
    break() {
        console.log('The Father has stopped the car.');
    }
}

for (const something of [new Parrot(), new Lambo()]) {
    if (something instanceof Animal) {
        console.log('It is animal!');
    }
}

So, if Object-Oriented programming requires a "Holy foursome" and not only a "Holy threesome", JavaScript still can be
considered as a language that supports abstraction through subtype polymorphism, just not on the language level.

Verdict

We have established criteria for evaluating if one language can be considered as a language that supports
Object-Oriented paradigm. By evaluating each language against each requirement, we can easily conclude that both
JavaScript and PHP are Object-Oriented languages, there is no doubt about that. All required boxes are ticked.

The last one seems debatable, JavaScript does not support interfaces and abstract classes on a language level. If we
formulate the statement "JavaScript prevents us from having interfaces and abstract classes", it would be false. We can
mimic them, we know that it is possible, and this article contains code that proves it. So if it is possible, it is
supported.

However, the most important here is that we introduced a systematic approach and mental model of how to evaluate if a
certain language supports a certain paradigm. This is important because there is a lot of fallacy when it comes to
functional programming paradigm and JavaScript and PHP.

NOTE: According to the Alan Key[16], the father of Object-Oriented
paradigm and Smalltalk programming language, we missed the key point of Object-Oriented paradigm, which is state
encapsulation and message passing[17].

Support for Functional programming paradigm

While in Object-Oriented paradigm programs are organized around objects, their composition, and their interactions (
messages), in a Functional programming paradigm programs are organized around functions and their composition. Origins
of Functional programming paradigm are in Lambda calculus, established by Alonzo Church in the
1930s[18], but the year 1950 can be considered the year of birth of the
Functional programming paradigm with LISP language.

Similarly to the Object-Oriented programming paradigm, a Functional programming paradigm has its own set of features
that a language must support to be considered a functional programming language. Those features are first-class
functions and higher-order functions
, referential transparency and immutability, pure functions, functional
data structures
, lazy evaluation, and recursion. There is also requirement for type system which can be
found in the literature, it will be evaluated in this article, although type system is a feature of the language that
prevents us from writing invalid programs which could error out in runtime.

First-class functions and higher-order functions

When language supports first-class functions, it means that functions can appear anywhere in the code, including being
passed as arguments to other functions, returned as values from other functions, and assigned to
variables[19]. Higher-order functions are functions that can take
other functions as arguments or return them as results[20].

Both JavaScript and PHP support first-class functions and higher-order functions.


// JavaScript
const add = (a, b) => a + b;
const operator = (a, b, operation) => operation(a, b);

console.log(operator(1, 2, add));

// PHP
$add = static fn ($a, $b) =>  $a + $b;
$operator = static fn ($a, $b, $operation) => $operation($a, $b);

echo $operator(1, 2, $add);

The examples above do not demonstrate a function returning function, but it is possible in both languages. So we can
conclude without any doubt that both JavaScript and PHP support first-class functions and higher-order functions. This
is a crucial property of a Functional programming paradigm as it is necessary for the composition of functions.

Referential transparency and immutability

Simply put, referential transparency and immutability mean that once allocated memory is assigned to some value, it can
never change, it can be freed only. This is important because it prevents side effects. There are nice consequences of
referential transparency which can be exploited:

  • No side effects - since memory is immutable, there are no side effects. This implies that there is "no (mutable)
    state". There is always a state of the memory, of course, but the state can not change, hence the slogan "no state, no
    side effects" of a Functional programming paradigm. This is important because it makes programs easier to reason
    about, easier to test, and easier to debug.
  • Thread safety - since there are no side effects, there is no need for locks, semaphores, mutexes, and other
    synchronization primitives. Every thread can simultaneously access a given memory location.

However, some notes must be made here, and those are related to some misconceptions about immutability and what is
allowed. So here are some expressions which are not allowed in Functional programming paradigm, but you will most likely
see in "functional" JavaScript and PHP code:


// JavaScript
function foo(a) {
    let b = a++; // Not a functional programming
}

Expressions such as a++, a-- are not allowed in functional programming paradigm, they are mutating the memory.


// PHP
for ($i = 1; i++; $i < 10) {
    // Not a functional programming
}

Loops are invalid in a Functional programming paradigm, they depend on mutable state.

JavaScript has const keyword which is used to declare readonly primitive variables, such as numbers, strings, and
booleans. However, const keyword does not make the object/array immutable, it only makes reference to the object/array
immutable. PHP does not have such language construct.

However, that is not important at all. If referential transparency and immutability are required for the Functional
programming paradigm, we can achieve it in userland, either through discipline, or by using linters and static code
analysis tools, or both. So if we can write our code without mutating allocated memory locations, we can say that
language supports referential transparency and immutability.

Pure functions

Pure functions are functions that have no side effects and return value depends only on its arguments. Since referential
transparency and immutability are required for a Functional programming paradigm, pure functions are a natural
consequence of those two properties. And they have some really nice properties:

  • Memoization - calling a function with the same arguments will always return the same result. This means that the
    function results can be safely cached, practically forever[21].
  • Thread safety - a natural consequence of referential transparency and immutability.
  • Easy to test - since there are no side effects, testing is easy.
  • Reordering of execution - If there is no data dependency between two functions, their order of execution can be
    changed, or they can be executed in parallel.

Neither PHP nor JavaScript has language constructs that enforce pure functions, but that does not prevent us from
writing them. Therefore, both languages support pure functions.

Do note that using memory addresses from outer scope does not make function impure. As long as there are no states and
no effects function is pure.


// JavaScript    
function foo(bar) {
    return function (baz) {
        return bar + baz;
    }
}

// PHP
function foo($bar) {
    return static fn ($baz) => $bar + $baz;
}

Both functions are pure, even though they are returning functions that are using memory addresses from outer scope. The
reason for that is that there are no side effects, and the return value depends only on arguments. Functions in examples
are thread-safe, can be memoized, can be reordered in execution, and are easy to test.

Type system

Type system in my own opinion should not be required for functional programming paradigm, but it is a useful feature
required for writing safe programs. PHP has (an optional) type system, to some extent (generics are not supported, for
example), while JavaScript does not have a type system. However, if type checking is required, it can be achieved in
userland (for example, via specialized tools for static code analysis, such as Psalm[22] and
PHPStan[23] in the PHP ecosystem).

Functional data structures

Functional data structures are immutable data structures. That would mean that both PHP and JavaScript would have to
have, for example, immutable arrays and objects. Neither supports that on the language level. This requirement comes
from the fact that functional programming languages that do support functional data structures are optimized for them
because immutability is expensive.

Again, no one prevents you to create and use immutable data structures in your JavaScript and PHP code. So, it is not
supported on the language level, but it is supported in userland.

Lazy evaluation

Lazy evaluation comes from the fact that functional programming languages are slower than imperative programming. Lazy
evaluation is a technique where expression is not evaluated until it is needed, which should improve performance.

Take a look at this example:


// JavaScript
let arr = [1, 2 / 0, 3];
console.log(arr.length);

// PHP
$arr = [1, 2/0, 3];
echo count($arr);

Both languages will throw an error, but in a functional programming language that supports lazy evaluation expression
2/0 would not be evaluated until it is needed (for example arr[1]).

Still, even though PHP and JavaScript do not support lazy evaluation on the language level, and can not be achieved in
userland, it does not prevent us from writing programs that use functional programming paradigm. The lack of this
feature should not be a deal-breaker for functional programming as it only impacts execution performances, and maybe
when a runtime error would occur due to an incorrect program.

Recursion

Recursion is a technique where a function calls itself. Since a Functional programming paradigm is based on functions
and does not allow loops, recursion is required. Both PHP and JavaScript support recursion. However, there is a problem
in both of the languages, and that is stack overflow.

The best way to explain this is through example. Let's say that we have some number n and we want to calculate the sum
of all successive numbers from 1 to n. If we have 5, the sum would be 1 + 2 + 3 + 4 + 5 = 15. The usual approach
would be to write a simple loop:


// JavaScript
function sum(n) {
    let result = 0;

    for (let i = 1; i <= n; i++) {
        result += i;
    }

    return result;
}

// PHP
function sum($n) {
    $result = 0;

    for ($i = 1; $i <= $n; $i++) {
        $result += $i;
    }

    return $result;
}

Given code examples violate a Functional programming paradigm, we are mutating memory. So, let's write a recursive
function:

// JavaScript
function sum(n, carry = 0) {
    if (n === 1) {
        return 1 + carry;
    }

    return sum(n - 1, n + carry);
}
// PHP
function sum($n, $carry = 0) {
    if ($n === 1) {
        return 1 + $carry;
    }

    return sum($n - 1, $n + $carry);
}

Now, these are pure functions which do not mutate memory. However, if we call sum(100000) in both languages, we will
get a stack overflow error. This is because both languages do not support tail call
optimization[24]. Tail call optimization is a technique where the compiler
recognizes that the function call is the last operation in the function, removes the current stack frame, and replaces
it with a new one. So if we have sum(100000) in tail call optimized language, we would not get a stack overflow error
because there would be only one stack frame.

Verdict

We have established criteria for evaluating if one language can be considered as a language that supports the Functional
programming paradigm. Neither PHP nor JavaScript supports all required features on the language level, but most of those
are either debatable (type system, lazy evaluation) or can be achieved in userland. However, due to a lack of tail call
optimization, we just can not safely write our application in JavaScript and PHP using a Functional programming
paradigm because the successful execution of the program depends on the input size. Only one recursive call with a large
input size can crash the program.

This must be impossible in functional programming languages.

Since this is not possible in JavaScript and PHP, they do not fully support a Functional programming paradigm. The
range of problems that can be solved safely using these languages with a Functional programming paradigm is limited.

Final thoughts

The problem with the lack of tail call optimization in JavaScript and PHP can be solved by replacing recursion with
loops. If we do so, we will have so-called "impure functions"[25] in our
functional programming code. Impure functions are functions that have side effects, that is, imperative style
programming is allowed.

If impure functions are allowed in a Functional programming paradigm, and if we are using them in our functional
program, that means that if we add pure functions to our imperative program, can we claim that we have an "impure
imperative" program (which is ridiculous statement)?

The whole point of a Functional programming paradigm and all the goodies which come from functional programming comes
from the pure functions and "no state, no side effects". Contaminating every (or most of the functions) with state and
side effects makes a Functional programming paradigm useless and claiming that we are writing functional programming
code is a fallacy. Unfortunately, that happens all the time. People are claiming that they are writing applications
using a Functional programming paradigm, especially when it comes to front-end development. However, if you take a
look at their code, state and side effects are everywhere. You will even find this keyword in functions with the claim
that functions follow a Functional programming paradigm.

Even when using a Functional programming paradigm, you will eventually have to deal with the state. How you will deal
with it will determine if you are using a Functional programming paradigm, or not. Think about that like spaghetti
(pasta). You love them, they are tasty, but it has dirt all over them. That is the state and side effects in a program
written by using "functional programming".

If that "dirt" is in the plate, but there is a little of it, and there is a strong boundary between spaghetti and dirt
(dirt is not sprinkled all over the spaghetti), most of the time you can enjoy spaghetti without dealing with dirt.

Knowledge is crucial as well. By knowing the limitation in JavaScript and PHP in terms of tail call optimization, you
know that this function is unsafe:


// JavaScript
function sum(n, carry = 0) {
    if (n === 1) {
        return 1 + carry;
    }

    return sum(n - 1, n + carry);
}

However, you can replace it with an impure equivalent:


// JavaScript
function sum(n) {
    let result = 0;

    for (let i = 1; i <= n; i++) {
        result += i;
    }

    return result;
}

From the outside world, this function can be considered pure. It is thread-safe, can be run in parallel, can be
reordered in execution, can be memoized, and is easy to test. But how you decide to implement it, will determine if you
are using a Functional programming paradigm. If a decision is based on the knowledge that JavaScript/PHP does not
support tail call optimization and input size can be large so stack overflow can happen, then we are talking about the
informed decision, and your function, even though it is not pure, can be considered as such.

If you have written it without knowing that, then you are just writing imperative code.

References

[1] https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)
[2] https://en.wikipedia.org/wiki/Prototype-based_programming
[3] https://go.dev/doc/faq#Is_Go_an_object-oriented_language
[4] https://www.youtube.com/watch?v=xcpSLRpOMJM
[5] https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)
[6] https://www.crockford.com/javascript/private.html
[7] https://en.wikipedia.org/wiki/Polymorphism_(computer_science)
[8] https://en.wikipedia.org/wiki/Ad_hoc_polymorphism
[9] https://en.wikipedia.org/wiki/Parametric_polymorphism
[10] https://en.wikipedia.org/wiki/Subtyping
[11] https://en.wikipedia.org/wiki/Object-oriented_programming
[12] https://en.wikipedia.org/wiki/Dependency_inversion_principle
[13] https://en.wikipedia.org/wiki/SOLID
[14] https://en.wikipedia.org/wiki/Duck_typing
[15] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
[16] https://en.wikipedia.org/wiki/Alan_Kay
[17] https://wiki.c2.com/?AlanKayOnMessaging
[18] https://en.wikipedia.org/wiki/Lambda_calculus
[19] https://en.wikipedia.org/wiki/First-class_function
[20] https://en.wikipedia.org/wiki/Higher-order_function
[21] https://en.wikipedia.org/wiki/Memoization
[22] https://psalm.dev
[23] https://phpstan.org
[24] https://en.wikipedia.org/wiki/Tail_call
[25] https://en.wikipedia.org/wiki/Pure_function

Last Update: August 28, 2024