> Just because the logic is organized as functions doesn’t magically make things fit together.

This is where you're a bit off. You're not technically wrong though. A function that outputs a string can't compose with a function that takes an int as a parameter, but this isn't what I'm referring to. Allow me to illustrate a shorter example.

Before we start off, we should both be in agreement that piping results from unix programs into other unix programs is a very lego-like method of programming, so if I can bend a program into following a similar style I've essentially "bent" the program into something that is "lego-like."

Say my programming task is to find out the value of (2 + 1) * 3 * 2. I will illustrate how to do this using a very typical functional style then using the point free style then using OOP and finally using unix pipes. The goal in every example is to promote reuse-ability to the maximum potential. With these examples you can develop the insight needed to see why functional programming is more lego like than other forms of programming.

functional style:

f :: int -> int
f x = (x + 1) * 3 * 2

main = print (f 2)

The above code is your typical functional program. Coded in a way that's not apparently re-useable at first. However, I look at this function f and I realize I can easily begin refactoring it piece by piece into something more re-useable / lego-like. Take "* 2" let's extract that first into a function called g:

g :: int -> int
g x = x * 2

f :: int -> int
f x = g ((x + 1) * 3)

main = print (f 2)

Then see "* 3" it's very easy to see how you can extract that into another function called d:

g :: int -> int
g x = x * 2

d :: int -> int
d x = x * 3

f :: int -> int
f x = d (g (x + 1))

main = print (f 2)

Then see "+ 1". Again easily extracted into a new function called m.

g :: int -> int
g x = x * 2

d :: int -> int
d x = x * 3

m :: int -> int
m x = x + 1

f :: int -> int
f x = d (g (m x))

main = print (f 2)

Pretty re-useable right? Let's rewrite the function "f" so that this becomes more apparent using the point free style:

g :: int -> int
g x = x * 2

d :: int -> int
d x = x * 3

m :: int -> int
m x = x + 1

f :: int -> int
f = d . g . m

main = print (f 2)

Note that now it becomes obvious. The function f is a composition of d and g and m. It fits to together like legos. Now let us consider the same thing but with unix pipes. How would that work? Imagine you have three programs each one takes an input from stdin does a mathematical operation in them and throws the result to stdout. Each of these programs is named after the functions shown above and given the same mathematical operations as defined above: g, d, and m. What does the unix expression look like?

$> echo 2 | m | g | d

So you see I've given you a very primitive example using very basic mathematical operations to show you how easily it is to convert functional code into unix-like code which in turn is very lego-like.

I chose a simple example and even then you may not realize it now but if your program follows the rules of functional programming even when it scales it will have the properties that I just illustrated to you.

Now let's talk about OOP. The primitive and canonical solution to OOP cannot be decomposed into anything that is re-useable without massive changes to the structure of the code. Even for the simplest cases.

Allow me again to elucidate by converting g, d, and m into the canonical OOP code:

class F:
def __ init__(self, x)
self.x = x
def addOne(self)
self.x += 1
def mulTwo(self)
self.x *= 2
def mulThree(self)
self.x *= 3

f = F(2)
f.addOne()
f.mulTwo()
f.mulThree()

print (f.x)

The act of mutation ties methods to context and makes it so methods cannot be reused outside of a context. None of those methods can be reused outside of the context of F. If you want to make OOP into lego building blocks you're going to have to divide things up into smaller classes. Which is not nearly as straightforward as the functional program above.

Ultimately you'll realize that you're going to be dividing your class into smaller classes and the context becomes less and less relevant until you have something isomorphic to functional programming. Let's try making the above re-useable.

Let's try taking out mulTwo and making that reuseable.

class F:
def __ init__(self, x)
self.x = x
def addOne(self)
self.x += 1
def mulThree(self)
self.x *= 3

class G:
def __ init__(self, x)
self.x = x
def mulTwo(self)
self.x *= 2

f = F(g)
f.addOne()
f.mulThree()
g = G(f.x)
g.mulTwo()
print(g.x)

Now for mulThree and addOne:

class F:
def __ init__(self, x)
self.x = x
def addOne(self)
self.x += 1

class G:
def __ init__(self, x)
self.x = x
def mulTwo(self)
self.x *= 2

class D:
def __ init__(self, x)
self.x = x
def mulThree(self)
self.x *= 2

f = F(g)
f.addOne()
g = G(f.x)
g.mulTwo()
d = D(g.x)
d.mulThree()

print(d.x)

You will note that I'm reusing the constructor, How can that be factored out? Inheritance.

class Base:
def __ init__(self, x)
self.x = x

class F(Base):
def addOne(self)
self.x += 1

class G(Base):
def mulTwo(self)
self.x *= 2

class D(Base):
def mulThree(self)
self.x *= 2

f = F(g)
f.addOne()
g = G(f.x)
g.mulTwo()
d = D(g.x)
d.mulThree()

print(d.x)

So let's look at things from a birds eye view. With the functional style I was able to refactor to 3 primitives all of which can be reused and composed.

OOP I was able to refactor it into 4 primitives with one primitive (Base) ultimately being useless outside of the context of this problem and 3 other primitives that rely on base as a parent. Additionally you will note that I need 6 lines of code to achieve the computation I need and the compositions hardly look lego like because they involve initializations and mutations.

Literally for even a simple simple example OOP breaks down. You cannot make it lego-like WITHOUT using functional concepts. Literally the next intuitive step if you were doing OOP is to write this:

class F:
def addOne(x):
return x + 1

Which is heading in a more functional direction as the class just becomes useless sugar around a method that ignores internal state and is basically a function.

Also let's keep this discussion civil. I spent a lot of time writing this out for you. I will read and respond to your response but if I read anything like "You're niave" or "your opinions are shallow" you can guarantee I'm not going to continue any form of discussion with you.