O
/ \
B A
/ \
C | D
If population C and D descended from O then yes there is a continuous spectrum from C to O and from O to D but not from C directly to D. There is an actual hard distinction from C to D and the next step after this is speciation. For the greater majority of mongoloids and caucasoids this is the division that exists, although there is a definite population that is the O race, C and D have deviated enough where hard lines between skin tone and genetic markers exist in a high enough quantity that a categorization can be made. const x = 1
def g(n):
return n + x
into this: def g(n):
return n + 1
Because a compiler can do this rewrite in the first pass and still produce programs with equivalent output the two snippets of code above are actually isomorphic or just different aspects of the same concept. You can even just call it syntactic sugar. namespace myNameSpace {
def add(x, y):
return x + y
}
def add(x, y):
return x + y
print(myNameSpace.add(1,2))
print(add(1, 2))
into: def myNameSpaceadd(x, y):
return x + y
def add(x, y):
return x + y
print(myNameSpaceadd(x, y))
print(add(1,2))
As you can see in a first pass a compiler can get rid of all the namespaces and still produce a working program. Which goes to show another isomorphism. Namespaces and functions are the same thing as just a bunch of global functions with the namespace prefixed onto the function name. module B{
def g(x):
return (x + 1) * 2
}
module A{
def c(x):
return A.g(x)*4
}
what if we want to edit g in B without effecting c in module A? Let's say instead of multiplying the output by 2 I want to multiply it by 3. Solution decompose g. Then recompose: module B{
def g(x):
return h(f(x))
//requires find and place refactor for all usages in module B.
def new_g(x):
return z(f(x))
//decomposition of g
def f(x):
return x + 1
def h(x):
return x * 2
//new behavior
def z(x):
return x * 3
}
module A{
def c(x):
return A.g(x)*4
}
Such decompositions can only be done if your program was constructed from pure immutable expressions. class A:
def constructor:
//does a bunch of random shit
def someMethodThatMutatesSomething() -> output
class B:
def someOtherFunctionThatNeedsClassA:
//cannot call someMethodThatMutatesSomethingwithout doing "a bunch of random shit" or even possibly modifying or breaking something else. Modularity is harder to achieve with this pattern.
versus: def somePureFunctionWithNoSideEffects(input) -> output
somePureFunctionWithNoSideEffectsabove does not need any hard lines of protection. There is zero need to use the antics of "deconstructing a monolith" if you structured things this way. Functions like this can be exposed publicly for use by anyone with literally zero issues. int add(x: int, y: int)
Remember as long as that add function doesn't mutate shared state you know it has zero impact on any part of the system other than it's output... you can replace it or copy it or use it anywhere.... this is really all you need to do to improve modularity of your system. object.verb(parameter)
is no different than: verb(object, parameter)
Because both features are equivalent I would say neither feature is OOP and neither feature is really FP. object.setter()
has no equivalence to FP and is unique exclusively to OOP. Even the vocabulary: "setters" is unique to OOP. Hence following this logic, if you're doing OOP your code will have methods and "setters" that mutate or set internal state. If you're doing FP your code should be devoid of such features. def add(x, y):
doublex = x * 2
doubley = y * 2
return doubley + doublex
functional: def add(x, y):
return y * 2 + x * 2
haskell (functional): add x y = let doublex = x * 2,
doubley = y * 2
in doubley + doublex
also haskell: add x y = (y * 2) + (x * 2)
Due to the fuzziness in boundaries there's really only a few things that are hard differentiators between the three styles. When you examine the delineation and try to come up with a more formal definition of each programming style you will find that FP is simply defined as immutable programming, OOP is defined as a programming style that uses subroutines to modify scoped external state and procedural programming involves programming that can mutate variables in all scopes from local, external to global. There's really no other way to come up with hard barriers that separates the definitions and stays in line with our intuition. add = (acc, x) => x + acc
m = reduce(add, [1,2,3])
m => 6
not decomposable by virtue of mutating accumulator: m = [1,2,3]
acc = 0
for(int i = 0; i < m.length; i++){
acc += m[i]
}
acc => 6
A few people realized that the post was generated by AI and they mentioned this but they were actively voted down.
I think there needs to be friction on the downvote because mob mentality is often wrong. Disliked opinions are still valid opinions that aren't necessarily wrong.