DEV Community

rkeeves
rkeeves

Posted on

Computer Science PvP: A Meta-Strat for countering lategame Functional Bro Dual-Wielder builds

Content Warning

TL;DR Very very very long post. Really really really long build up. Reward might not be worth the effort at all.

This post is about going after lategame FP Bros, aka definitely not zero-gear React devs from arrow function tutorial island, nor Rust fold-ers with not yet maxed out skill trees.

I want to lay it out plain and clear: The goal is not "having fun", but to defeat FP Bros. In order to defeat your enemy, you have to understand your enemy. So... there will be an insufferable amount of lambos, champagne, guccis, workout routines and all kinds of FP Bro reflexes left from an earlier era of FP Broery. But, this is essential, because otherwise you might not understand why they name their venture capital firms the way the do.

Also, there will be alphas and betas... but before you start getting your hopes up about this being finally an enjoyable post from me... Nope, the alphas and betas we're going to talk about, have more to do with your IDE than your personal life. But, if you stick to the end, you might get an understanding of what Computer Science's endgame PvP content is all about. Actually, this is a Dev.to post by me, so you will probably get a shoddy, cheap, watered down version of the real deal.

In this post, you'll learn the secret technique for easily handling any lategame Functional Bro Dual-Wielder even if it has gear+skills maxed (the entire post was made because a high level player was taken out). It's kind of meh early-mid, but dominates late game.

Unfortunately, you'll have to pay a heavy price for it.

You'll lose something which you hold very dear to your heart.

Taking an Aristotelian walk under the olive trees

Somewhat before Andrew Tate started arguing about alphas and betas, alpha and beta were letters in Ancient Greece.

Ancient Greeks were weird, as they didn't have a concept of toilet paper.

But, just like us and Andrew Tate, Ancient Greeks had arguments too! Simply they didn't have social media yet.

I want you to picture a huge winding road under the olives and other trees.

I'm asking you to have a little walk with me along the road, looking at the trees and the birds.

To make the walk more entertaining though, I want to have a small chit-chat with you as we walk, to spend the time. Let's call it an argument.

Koinos topoi

You don't want to have an unpleasant walk do you?

Do you want to come along at all? Am I a pleasant walking companion at all?

So, before embarking on the walk, I will establish the koinos topoi aka common ground.

If the koinos topoi is acceptable to you, then I'd be very pleased if we could walk along together.

Otherwise if the koinos topoi is unacceptable to you, then we'll politely wave goodbyes and walk on our separate ways.

You always have the right to not accept the koinos topoi. It does not make you dumb or ignorant. It just means that we lack common ground, so walking with me wouldn't be fun for you. That's all.

Now, I will lay out the koinos topoi for you, so that you can skip the walk guilt-free if you feel like so.

I created a good-intentioned straw man for my argument.

I will refer to you as Pythonista Imperative Warrior. It would work just the same way with JS, TS, Swift, Go, Rust, Ruby, COBOL even C, list goes on.

I simply literally had to pick one, to demonstrate source code.

First, I assume that you Pythonista, hold the following two treasures dear to your heart:

  • Getting The Answer (if possible of course!).
  • Using Python.

Secondly, I also assume that you have at least one rule when it comes to Programming:

  • You don't take Computer Science advice from variety.com level stuff.

I built my argument on these things. If you cannot accept these, then my argument will fall apart and vanish into the wind, so it won't be a fun talk for you.

Now, it is your choice:

  1. Accept the koinos topoi and continue.
  2. Or do not accept the koinos topoi, and walk away freely without regrets.

In case you did not accept the koinos topoi, good bye, farewell, and enjoy the garden, my friend! I truly mean it! :)

Exordium - The Hook

If you are reading this, then you accept the koinos topoi, so now gloves are off.

My friend, the only sensible way to live in this world is to live without rules.

And tonight you're going to break your one rule.

Your gonna have to play my stupid little game to save one of them: Getting The Answer (if possible of course!) or Using Python.

Yes. What I said to you probably immediately sounded suspicious:

Did I hear it right?! ToNIGHT?

What kind of olive grove walk are we embarking on during NIGHT?

Now this is your last chance...

If you don't want to come along, you can opt out still. For example, checkout the post When React Starts Acting Like jQuery (and You Can Totally Tell 😅). It is a cool, insightful and fun article!

If you stick with me though:

I kind of lied a little about the olive grove... This aint gonna be a walk in the park.

And night?

Yeah, this ain't gonna be a React Functional Component Todo app.

What we're going to do - my imperative warrior teammate - is going to be something different.

We'll go a bit deeper than .map(), in fact, much deeper.

Nope... SOLID, Agile, SCRUM, OOP, Borrow Checker, Inheritance, Functional Style, Iterators, Traits won't help, you can leave them here.

We'll descend upon the darkness, so you'll need a different kind of gear from the trunk for this one.

Gear Check

So, as I told you, I will use Python as a straw man.

This is a night mission. You're gonna need Night Vision Goggles.

We'll see whether it works correctly. So first, setup some dummies, to check out the goggles.

You can have functions in Python like tofu:

def tofu(x):
  return x
Enter fullscreen mode Exit fullscreen mode

This can be rewritten into Functional Bro style like brofu:

brofu = lambda x : x
Enter fullscreen mode Exit fullscreen mode

So, let's check the Night Vision Goggles:

brofu

Now let's see whether the resolution is calibrated well enough with foo:

def foo(x):
  def tofu(y):
    return x
  return tofu
Enter fullscreen mode Exit fullscreen mode

In Functional Bro style, this can be written as broo:

broo = lambda x : lambda y : x
Enter fullscreen mode Exit fullscreen mode

Okay, the Night Vision Goggles still work:

broo

This registers on our target tracker as Lambda Abstraction:

abstraction

Always the closest Lambda to the hostage is grabbing the hostage:

binding

Okay.

Now, onto a Developer Experience thing: The most important Pre-Commit Activity.

Before commit, we regularly change variable names from iHateMyJob to something more Agile:

renamesymbol

The IDE Rename Symbol functionality is called α-conversion. Codes which are identical except for param names are α-equivalent:

alfa-equivalence

Yep, as I told you, these alphas won't help you much with getting a phone number.

Okay, thus far Night Vision Goggles work. Now, try moving your head.

We can apply functions to arguments:

(lambda x : lambda y : x)(1)
# In normal code, this would be
#   1. Define broo
#     broo = lambda x : lambda y : x
#   2. Apply broo to 1
#     broo(1)
Enter fullscreen mode Exit fullscreen mode

This is called a λ-application:

application

This is not the "evaluation" of the function applied to the arguments though. Aka it is just the name for the unevaluated thing.

The actual evaluation/computing is called β-reduction:

beta-reduction

Now, you'll see why you need "Rename Symbol" in IDE, aka α-conversion:

beta-reduction-capture

Solution is to simply "Rename Symbol" in IDE:

capture-avoiding

I'm not going to go into automatic renaming (De Burijn etc.).

This is fine for us, imperative warriors.

Let's shoot 'em all! But... which one to shoot first?

Theoretically it does not matter:

church-rosser

In practice the order of shooting does matter, because by choosing the wrong way:

  • Might take infinite time.
  • Might take infinite resources.
  • So you can shoot yourself in the foot.

Therefore we need some kind of strategy.

Let's introduce you to the Redex Thermal Camera. It's gonna be dizzy first:

thermal-camera

For fire coordination we're gonna use:

  • leftmost / rightmost
  • innermost / outermost

inner-outermost

As I told you earlier, to arrive at the thing I have to show you, we have to descend upon the darkness, my imperative warrior comrade. This is why you need these gogggles.

So the question is: Which one to shoot? What is our strategy?

In this discussion, we'll not talk about righmost, we'll always pick lefmost.

But, that still leaves us with two options:

  • Leftmost Innermost or Leftmost Outermost?

Making this decision is called choosing a Strategy.

I have zero clue, so let's pick Leftmost Innermost as that's the first one in the order.

Yeah, picking a strategy blindly is a bold move.

Let's see how it pays off!

Leftmost Innermost - Applicative Order Reduction Strategy

So, we are Pythonistas, let's do some real coding:

def foo(x):
    def bar(y):
        return y
    return bar(x)
Enter fullscreen mode Exit fullscreen mode

Now what would happen in python if you evaluated that in Functional Bro form?

py

It did absolutely nothing (besides printing that it feels).

Let's add a print there... maybe it does something which we didn't learn in CS 101:

pyeffect

It still didn't do anything, because it is still a function obviously...

So, let's see our strategy:

aor

Holy sh... it ran the function inside the other function!!!!

Yeah, you could say it is "smart because it did inlining" or a new Windows autoupdate feature, but come on bro:

Do you want uncalled code to suddenly start doing stuff? This should work as a language not a walking talking Skynet!

Well, this didn't go well.

Let's try the other way! The outermost....

Wait... that's not gonna change anything at all...

nor

Yeah, so... we are stuck, bugs out both ways.

So, let's do what we engineers do when a problem arises: Cheat.

Leftmost Innermost, not going into lambda abstractions - Call by Value Strategy

Let's stick with the Leftmost Innermost, but...

We no longer reduce to β-normal-form, but instead a weaker version:

cbv

So it solves our problem well, as it does not "run" uncalled functions:

cbv-example

This seems to be the one!

Omg, we nailed it - almost - first time! Now this is what I call Behaviour-Driven Development! We are awesome!

Let's try to program with it!

A Simple Program

Let's program... a simple function func: from the two args it has (x and y), it will give back the second one y which will be a doggo 🐶.

Yeah, I know dumb, but, let's try it!

stuff = lambda x: (lambda y: y(y))(lambda z: z(z))

func = lambda x: lambda y: y

func(stuff(0))("🐶")
Enter fullscreen mode Exit fullscreen mode

Entering this causes a stackoverflow:

cbv-python

Let's see the reduction chain:

cbv-loop

It happened because - I cooked up that stuff(0) to return a nasty thing - stuff(0) essentially returns something akin to a while(true) bomb:

def stuff(x):
  while true:
    # noop
  return x

def func(a, b):
  return b

func(stuff(x), "🐶")
Enter fullscreen mode Exit fullscreen mode

The lambda application stuff(0) returns a thing which, every time you reduce it, it yields itself back. It is basically a infinite lambda loop.

So that's it! No galaxy ending problem: There was an infinite loop in the code. Pretty predictable and obvious that this happened.

This is simply how computer programs work:

  • Python,
  • JS,
  • Go,
  • Rust,
  • Groovy,
  • OCaml,
  • Java,
  • PHP,
  • C etc.

Well, of course, each language is deeper and more convoluted in reality, and they don't do what we are doing with the funny symbols, but they work this way.

So we just learned how Python, Java etc. works from bird's eye view.

Of course, from Functional Bro perspective. Remember: Goggles on, soldier!

Ok, just for completeness' sake, let's quickly skim through the other strategy, then call it a day and go back to Imperative Land!

Normal Order Reduction Strategy

We'll pick the Leftmost Outermost and feed the doggo code into it.

Nope, we will not go fancy, we're gonna even allow reductions in lambda bodies.

So let's do it quickly:

nor-loop

Whoa there!

This didn't just finish, but reached the real solution, the β-Normal Form.

Yeah, not even the shoddy Weak Normal Form but the full deal β-Normal Form.

This strategy is called Normal Order Reduction Strategy, because it guarantees a solution (β-Normal Form) if such β-Normal Form exists.

You are probably thinking: Okay, but what if that while(true) was my game loop which did something important? Wouldn't this break my program?

And here's where I will ask from you:

In the below picture, how many statements, actions of effects do you see?

pure

None.

If a pure function has no effect, then why would you compute it?

Seems like a mic drop like rhetorical question at first, isn't it?

Of course in imperative languages, like React Functional Components where things mutate and jiggle around Call by Value is the right choice.

But there are actual cool FP languages (ML family) which use Call by Value strategy.

Is the mic drop correct, or is there maybe a good reason for not using Normal Order?

Let's see a simple example, and show you that it double dips:

doubledip

Yes! Instead of passing down the computed value of the argument, it passed down the expression of the argument, and substituted that in two places! It doubled the workload!

Here's a simple example

def doubleit(x)
  return x + x

doubleit(1 + 1)
Enter fullscreen mode Exit fullscreen mode

In Python, this first evaluates 1 + 1 to 2 then it passes down to doubleit, so doubleit simply does 2 + 2.

In Normal Order, 1 + 1 would be passed down.

So doubleit would result in (1 + 1) + (1 + 1).

This is terrible. Waste of resources, waste of time, nonsense.

And we're not done yet... I will add more gasoline:

Remember that we purposefully allowed it to go into lambda bodies too? Remember the Skynet thing?! How uncalled functions wiggle and jitter?! This does that too!

And this is the point I was trying to make!

You value two things:

  • Getting The Answer (if possible of course!).
  • Using Python.

Yep, you can either choose:

  • Getting The Answer (if possible of course!) with Normal Order-kind of jankyness.
  • NOT Getting The Answer (if possible of course!) but you can keep using Python.

But... I also said...

You have at least one rule:

You don't take Computer Science advice from variety.com level stuff.

And that is the primary aim of this post.

Entering the Functional Bros' cave system

Now let me introduce you to tonight's secondary VIP guest: Scottish actress Kelly Macdonald.

There are two very important things why Kelly Macdonald is important for us.

The first thing is that, she has a unique take on our while(true) problem.

Let me tell you the real game behind the while(true).

In the story of No Country For Old Men, there's a maniac called Anton Chigurh.

This character ends the life of people for no obvious reasons. He does this by:

  • Flipping a coin.
  • Asking the victim to call Heads or Tails.
  • If the victim guessed right, the victim lives.
  • If the victim guessed wrong, the victim will not live.

Victims in the book always begged for mercy, but eventually they realized that Chigurh is adamant, so they all made a call, hoping in their 50% survival rate.

But, in the movie, one single character does something else.

Yes, it is non other than Kelly Macdonald, and she says:

"The coin don't have no say"

This is the Normal Order philosophy!

And it is present in almost all languages, in one way or another. Yes shortcircuiting!

With True it of course goes brrrr, but with False, it returns without messing with the infinite loop:

shortcircuit

Yes, and you know what more? In a philosophical sense your Python program is literally a testament to not doing stuff if not necessary:

#!/usr/bin/env python3

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

But why would we need Kelly Macdonald for explaining us how "shortcircuiting and / or" works? We already know how!

Yes, it isn't about and / or. We have much much bigger fish to fry...

We came here, to dig down into Functional Bro territory, to the place where all the nonsensical doodads, thingamabobs and codswallops come from.

The second important thing about Kelly Macdonald is that:

Kelly Macdonald was born in the same place as The Real Python (unrelated to the snake-symboled Dutch language by the same name).

Lazyness

Is there an Academia zero day exploit, so you can be a professor without PhD?

The answer to the title is surprisingly: Yes, here's someone who pulled it off.

That man, my friend, is a Master exploiter: Academia, any % PhD.

Now, we said earlier that, Normal Order guarantees solution (if there's such) but there are two problems:

  1. But it douple dips.
  2. And it goes into lambda abstraction bodies.

So, that exploiter gentleman and a lot of others sat down in the 1980-1990s... and... you guessed it right... they pulled off the exploit.

The exploit is called lazyness, and they made a language out of it called Haskell, and this is where most of the codswallop comes from (some of it comes from LISP/Racket, a little bit from Clean too, and to a degree OCaml, but Haskell is where most of the meme-level stuff comes from).

We're not going to spend too much time on the double dipping hack, but I'll give you the basic problem.

After that we're going to see the hack strategy Call By Name which does Point 2, aka no lambda body touching (and a bit more).

The Double Dipping Problem from Bird's Eye View

So, the double dipping occurs, because what we are doing should not be a tree, but a graph instead:

graph-rewriting-systems

But graphs are problematic:

graph-hard

The solution is simply a Proxy like thingy (in theory):

graph-more-abstractions

Once again, in theory. In real life the implementation of this requires Professor level knowledge, and even with the best efforts it is a very delicate and hard to control system.

It is really smart, I don't want to attack it, but as a layperson it seems to be hard to easily reason about actual runtime behavior.

Despite its genuinity, would you like to make a Mars Probe with this, or rather good old C?

Looks a bit crazy...

Back to Kelly Macdonald: Is she a good actress? Yes. Would I pick her Trainspotting movie for your company's Christmas Party? Probably Not.

Let's move on to the other topic.

Call by Name Strategy

So, can you guess what the strategy is?

Yes! You are right: Leftmost from Outermosts, Don't go into abstractions.

The normal form is Weak Head Normal Form and the strategy is Call by Name Strategy:

whnf

But what does it do differently than Normal Order?

Let's see first a simple example:

cbn

Above you can see that Call by Name computes only "what is necessary at the curent stage", and by stopping at the lambda, it does not enter the infinite loop.

To see another effect, let's consider reducing with Normal Order this thing:

nor-ctor

Yep, it will infinitely loop.

Now, let's see with Call by Name, and this where it might make sense, that the Weak Head Normal Form definition requires recurisve thinking:

cbn-ctor

What is this good for?

Honestly: I don't know, I have donkey brain :)

But, here's s food for thought...

In Python you can't really do digits = "0123456789" + digits:

pyrec

In Haskell you can though. You don't generate the infinite list upfront, but it gets produced on demand, as you can see below puking out digits onto the screen (aka it is literally an infinite list being printed):

hsrec

Aka, lists can be thought of as streams generated on demand, Optional things (Maybe) can "store" things without computing them etc. Below we take the first twelwe of infinitely many digits which repeat cyclically:

hsrec2

Above thing: is Bring 12 wolf hides by farming the inifinitely spawning Lvl 1 Wolves.

Yep. Not exactly ESports.

Yet.

I'm gonna cheat

Before we go further: I'm gonna cheat.

There's a way to represent booleans, numbers, addition, subtraction and branching just with lambda abstractions and applications and variables.

But I ain't gonna do it here. I will add them as primitives to the language.

Why? Because 2 + 1 would be too easy for us?

Nope, quite the opposite, below is a Call by Name reduction of a simple 2 + 1:

churchn

As you can see it can quickly get out of hand.

The above puke-y thing is not Haskell.

It is a tiny code written in Haskell, which simulates pure lambda calculus, which we've been doing all along...

Yeah that's right: We are now Imperative Lambda Boyz too!

Obtaining full gear with Recursion

When there's grinding there's always a need to automate it.

But when to stop the grinding? How does grinding work?

grinding

Eventually you'll reach a point where more grinding will not improve you in any way. You have the armor.

So... functions map points to points.

If a function takes Point x to itself, then x is a fixed point.

fixed-point

Aka, we somehow need an autoclicker which stops if it no longer makes progress:

stop

Of course our naive implementation cannot stop:

howmanymore

So we are going to add a stop, and I will rename it to f for conciseness' sake:

howmanymorerec

Yay looks awesome on paper! Nope, immediately bugged:

selfref

So, let's supply the next function to call as an argument and hope it works:

noselfref

Nope. The problem didn't disappear! We just tucked it away!

So let's try hammering away at the actual solution.

For a second, forget that you are a programmer, and think about just the Night Vision Goggles and the Thermal Camera.

We want to find a normal form.

We literally want to hammer away at the thing until it no longer changes.

Yep, we want to auto-hammer it 'till it stops changing.

But how to auto-hammer? Remember that while(true) bomb from earlier? It could run inifinitely:

loop

But it would stop, if somehow the result would not give out a heat signature...

Now suppose we had a stupid function there:

f

It would sometimes cut the whole loop by returning 1 (remember it is lazy, so wouldn't compute its argument the 🐶, if it already knows to just return 1):

f-base

Hmh... In theory this could stop. The problem is that the other code branch loses our "clown box" function with the cute doggo 🐶.

So we need to keep the doggo 🐶 on the right side too, to not lose it:

f-rec

This would still return 1 sometimes, but also retain the doggo 🐶 for later use.

f-branch

But what kind of stupid function would:

  • sometimes give back a number,
  • sometimes give back an evaluatable expression?

Well, our stupid function is just like that:

sometimes

And my imperative warrior teammate...

...this is the Curious Tale of How...

This "sometimes" is still the workout song for FP Bros since the 1930s.

(at least theoretically, as I'll show you reality later).

It is called Y-Combinator (No, it isn't referring to Y-chromosome, it's not a men-only dating app). FP Bros with lambos, booze and loads of cash still call their companies Y-Combinator, because it burned into their cortex in the gym.

Oh... and 1930s... It isn't a typo. FP Bros've been hitting the gym, listening to Flo Rida, since the purist/cleaner guy at the gym showed them how to lift, see?

y

They like this, because it enables them to do this sort of calling yourself magic, without introducing self-reference.

I'm gonna show off: n=1 ====> n=0.

I'm not even going to guide you through it step by step as it is unnecessary flexing for us.

Just enjoy the show:

y-chain

In real implementation of the language, they are not doing it this textbook way, but for us this is enough.

Because: This is what recursion is.

Now comes the question: Can you do this in Python?

No! Remember that all of this hinges on NOT EVALUATING ARGS unless necessary.

So you can't do Y-combinator in Call by Value (Python).

Finally, if you had even the slightest shadow of a doubt about this entire thing being more than just Gucci bags and lambos...

Here are the actual names:

combinator

Yeah, they went - in typical Bro fashion - with the name Bottom:

bottom

The ultimate kryptonite of FP Bros is: Once they see a bottom, they can't take their eyes off of it (It is not their problem. We are just like them. A bit later I will argue that we are one and the same in this respect, but I don't want to talk about it yet.).

Dual wielding, maxed out skill tree

I said we are going after Dual wielder FP Bros, because...

Normal FP Bros have Lazyness in main weapon slot.

In the other hand they hold the "This is just a classroom program to demonstrate XY" Shield.

The problem is that lazyness has cost.

Imagine the following Python code:

x = 1
x = x + 1
x = x + 1
x = x + 1
x = x + 1
print(x)
Enter fullscreen mode Exit fullscreen mode

You, as a normal sensible person, expect:

x = 1     # x = 1
x = x + 1 # x = 2
x = x + 1 # x = 3
x = x + 1 # x = 4
x = x + 1 # x = 5
print(x)
Enter fullscreen mode Exit fullscreen mode

Now, the main idea behind lazyness is to not do the thing unless necessary.

x = 1     # x = 1
x = x + 1 # x = 1 + 1
x = x + 1 # x = 1 + (1 + 1)
x = x + 1 # x = 1 + (1 + (1 + 1))
x = x + 1 # x = 1 + (1 + (1 + (1 + 1)))
print(x)  # Only when it is forced does it reduce to 5
Enter fullscreen mode Exit fullscreen mode

A good problematic area are "lists", as they are not arrays but... more like streams which are not ephemeral (once you generate an element it remains).

list

Let me introduce you to Little Boy (I didn't want more bottom jokes).

Little Boy does one thing, but does that consistently:

littleboy

Little Boy blows up if the program evaluates it.

For example, to print the list, the list must be evaluated including Little Boy:

bomb-list

If it is needed it will blow, otherwise it won't be evaluated. Below we never need to evaluate Little Boy so it doesn't go off:

take2-bomb

Lists are more like, I don't know a grape vine or something with a bunch of graphs and bombs:

list-elements

Yep, even that is a lie, because even the list itself can lead to a Little boy:

take2-bomb-empty

So it is more like something hanging in the air:

list-hanging-in-air

Because as you saw earlier, it is computed on demand:

infinite

Aka it is just a Lazy Constructor:

node

I think you get the point now:

Retaining Mother of All 1 + 1 + 1 +... Expressions in memory is bad for you.

So how do they handle it?

  • Main-hand: Call by name and "laziness" (non-strictness),
  • Off-hand: Strict hammer

Yep. They are doing what you are doing:

  • Your language: Strict by default, pain to make it non-strict.
  • Their language: Non-strict by default, pain to make it strict.

Yep, they are literally forcing values, unlike you, because for you that's the default!

I don't want to go into spine and element-wise strictness.

They'll probably say that "Yeah, but lists are blabla", nope, their constructors are non-strict by default. List, Maybe, Either etc. They can be made strict, yes, but then it is no longer "lazyness with style" is it?

The only flex they have, is their compiler is really smart (Yes it is a compiled language.). So it optimizes away stuff if it deems it safe. Aka: if the program sees through the junk code what the author actually meant, then it can optimize away fluff. One example from the many is how their temporary lists become essentially for-loops via deforestation with shortcut-fusion (Deforestation in the graph sense, not the Forrest Gump sense. If the code is dumb, they can't do any magic.).

They didn't solve all the Universe's problem. They just have a different default than you.

Computer Science's PvP: How by counting cows, we visited Hell with a selfie stick

If you made it this far, congrats!

From this point forwards we won't be doing anymore calculations.

Now it is time of Story Time!

I want to talk to you about what happened when people decided that it is a good idea to refactor Math. But first I want you to be able to feel the need for it, so you won't think they were dumb for trying to do the refactor.

So before we can talk about The Mother of All Refactors, we have to talk about The Mother of All Feature Creeps.

Originally math was supposed to Count cows:

sumer

Of course, when you sold cows you had to somehow add cows to the target, substract from source:

babylon

But sometimes you had to count bushels of wheat too, and there's too much bushels to always write down, so people started using shorthands:

egypt

The greeks started trading, and there were just too many coins even for bushel counting, so they introduced even more abstractions:

greek

Then came the Romans, who really digged taxes and taxrates:

roman

Of course, people had different kinds of coins in circulation, and they had exchange rates too:

roman2

While this was going on, Indian devs - who were really ahead with some commits on an experimental branch - added dot to their branch, which kind of meant: nothing.

That branch came into contact with western branch via people who - in my opinion - had an eye for making the source code look poetic:

arabic

Of course, westerners wanted to merge that whole thing, but their first attempts at zero were... a bit lackluster at best:

western

But eventually everything, even the non-zeroes made it into the western branch:

german

Fast forward some years like to 1718, and people were using this system for real-estate sales in Mexico City:

realestate

Another feature was for example solving equations. Here's a formula which does just that:

quadratic

Of course the Chinese also worked on writing down followable explanations on how things work, for example:

chinese

And westerners had their own style too:

notchinese

After the arabic numerals, things started looking a bit more modern and you can already notice numbers and division-ish straight-ish horizontal lines, of course with a lot of insane derp peppered around:

algo

With a dash of comments, it actually kind of started looking quite jazzy:

comments

Of course, by the 1780s it went really crazy, as the complexity kept increasing exponentially:

exp

You might ask at this point: Where is the parentheses?

Here's (

paren

Most of the time it wasn't really useful, because Pros just "felt the code":

paren2

There was a guy called Clavius in like 1600s or something, who kind of accidentally did ) too:

clavius

This double-whammy () symbol pair wasn't really insanely welcome to the repo, but eventually all the symbols we know made it into Math...

Of course, only after trying out each and every possible wrong way first:

failedattempt

So, now we are in the 1900s. New century, new things to come, bright future ahead, and a large legacy enterprise repo, and there's always a need to add new features though.

And here's the important point why I made you suffer through that syntax:

  • I was just talking about syntax here.
  • Math is 99% semantics, so imagine what kind of tomfoolery was going on!

I'll just give you two examples of what kind of state the codebase was in.

First one is what should Edward Scissor Hands do?

barber

Answer is: no answer.

Second one is about shopping items from NPCs.

banachtarsky

Yep, that's an item duplication glitch right there! No, I'm not kidding! Things were really mental.

Yep, so Math kind of started glitching out...

And at the rate they were going with new features, this was shaping up to be The Mother of All Release Days.

There was a 10x Dev though in a white hat:

hilbert

And this "Hilbert" guy just had enough of the bugs. He manned up and he came up with a bold plan.

His Plan was not just mathematical, but a noble social Agenda:

Let's band together all fellow mathematicians, and refactor the entire codebase.

He wanted to build a Tower of Babel from the ground up, to pierce the clouds of the High Heavens.

And this was a good call! The most noble, most well-intentioned plan of all time.

At the worst possible time.

Because there was a guy called Kurt Gödel.

There's always that guy. Kurt Gödel was exactly that guy, you know, the one who you'll not tell about the company teambuilding trip and hope you and the others can have a wonderful time without him.

Kurt Gödel had two character traits:

As a true German, even though he spoke other languages well, he acted like he knew only German (this Germanic tendency will come up later):

godel

Gödel did the following basically...

He git cloned the entire Math repo...

He listed the contents of the repo:

godel0

Did rm rf, even explicitly saying that everything goes out of the window:

godel1

He made sure that it was deleted:

godel2

But he did not stop.

Gödel added a and saw a different listing:

godel3

../ was pretty self-explanatory: There are things outside Math like the paper on which the proof is written. Easy-peasy.

./ was pretty self-explanatory: A friendly reminder that Math itself is a thing too! Easy-peasy.

But what is .ignoreme?

So Gödel did what the file name and cinema tropes literally warned against:

Gödel didn't ignore it. He had to touch that file.

He could not look into it due to no rights, but he was able to make a statement about it: just a tiny tap, a little mathematical pat on the thing, if you will.

Gödel was able to make some statements about it.

Noone has any rights to the .ignoreme, but it is not empty.

To just give you the handwavy feeling: Let's say you've been working 20 years on a problem. Let's say you missed your daughter growing up for it. How can you tell whether your problem and the solution to the problem is in the .ignoreme? You cannot prove or disprove, only by working out the answer yourself. If you didn't find the answer yet, you'll never be able to find solace, because it very well could be lying out there for you to discover or... it might be the 69th entry in the .ignoreme so you can never find it. And you messed up the most fun years of parenting, and the most you can get as a Math Bro is a maybe. Is this just? No. Is this fair? No. Is this reality? Yes.

Gödel's work - aside from these philosophical problems - unleashed a earthquakes, eruption all across Math, and drew even more attention to the gaping hole at the foundation of everything.

So: Gödel didn't have to refactor! :) Job done!

"Bbbbut what happened to Mathematics, the demons, the meltdown?"

We are engineers and our job is not to understand, but to weaponize. Chaos? I smell opportunity.

We are done with the math part.

Now comes the Computer Science PvP spin of the story!

So imagine, you are a math/science guy with a keen eye for engineering, and you saw all of this unfold. What would you say?

What any sensible math/science guy with a keen eye for engineering would say.

Gödel used a bunch of symbols and operated on them. This is what essentially Math is (symbols - syntax, definition and rules which govern them - semantics). The symbol flipping technique itself was quite interesting, so people asked: How can you harness the powers of the literal Abyss?

Enter Alonzo Church (gym cleaner heavy-lifter guy from earlier) and his student Alan Turing. They were also circling around this Plains of Anguish idea on their own ways, but with a very practical edge: If you make a system which can manipulate symbols into other symbols, then what you are really doing is formalizing calculations. Not necessarily a computer, but an abstract system which formally describes symbol juggling.

Aka, creating - an abstract - machine to compute. Not for any kind of physical goal per se, but to nail down the details theoretically, so this "machine" can be used as a vehicle in arguments for example.

They developed two different, yet equally powerful, techniques/machines to manipulate symbols:

  • Turing: Turing Machine
  • Church: Lambda Calculus

These both are really cool, but they are also literally balancing at the very edge of the Abyss, and this is why we have for example Halting Problem and this is why even innocent things can turn really ugly real soon.

Now, do you remember the Bottom that the FP Bros kept looking at? It might seem funny, but this is not a funny cute girl's lower half, but reality showing its really nasty underside. This is present in Python too! Both Gödel, Turing and Church were able to arrive at the same absurdity and existential dread.

They were able to arrive at the same place as Gödel, and they were even pen pals! Yeah! Guys were absolutely having the fun of their lives at the Gates of Hell, and they were snowballing with the ashes of humanity's hopes and dreams.

Gödel, Church, Turing. Truly three nice, decent, clean-cut young men just doing good old Math.

The two machines (plus the nightmarefuel pump of Gödel) work differently and approach the same problem from different angles.

Add some decades of infighting between the fans, some Stackoverflow, Reddit and personal, career and financial interests to the mix then... we have PvP.

I want to hammer home that all of this thing started by: Someone wanted to just count cows.

I can bet, that you have never ever seen feature creep on this gargantuan level. PHP, C++, JS is nothing compared to this.

Sorry for the long-winded route we took, but I just wanted to demonstrate, that as simple coders we are building incomplete, unsound, weapon systems which are dancing at the brink of the vast plane of absurdity.

Yeah, so gun manufacturers split into two groups:

  • FP Bros are Church acolytes,
  • while others are more Turing (and von Neuman) fans.

But there's always a silverlining: now even I can write programs. (And I at least had the patience to learn a programming language. Now we have LLMs, which answer the only remaining question of the Computer Science Industrial Complex.)

Haskell is really close to Church and of course Haskell Curry. Haskell is really trying hard, and that is admireable. Haskell - due to its evaluation model and philosophy - is really close to the real deal.

But...

You are probably like me, aka not an expert. So most of the stuff you'll see at first is not dual-wielding code:

primes

The unspoken real goal of these is marketing. They just don't write out "We have warts too just like COBOL".

A less successful marketing slogan would be: "I do have serious warts. But maybe you are the kind of person, who can accept or maybe even like my warts. And you have warts too, so I'm going to make a compromise too and accept yours."

This slogan would never fly, and this is how you get the famous sort:

quicksort

For a time this was shown off in a couple of places as marketing, but now they at least took it down. They are not doing it in-place, they are copying, so it is marketing.

I'll show you what they do instead though, because friends tell the truth. (or at least they try, but I'm dumb)

Here's an example, of their merge sort in base (kind of like a standard library thingy) where they exploit existing runs aka ordered subsequences:

run

Not so fun anymore, is it? What are those ! in the code? Yep, they are dual-wielding strictness in off-hand, they are forcing things to evaluate.

And this isn't even Monad Bro code! There are warts which you can either tolerate or leave. This is why I started with the Content Warning and the koinos topoi.

So in this regard both camps are pondering and marketing. Not each individual, just the groups.

Once again, not individual people. There are some mighty cool people on both sides.

For example, FP Bros made a lot of cool things on the side too, which either made it to your language, or didn't, but was academically significant. I mean they tried it out in Haskell (a research language), it flopped, so now we know it is a bad idea. Example: add sequent calculus to Haskell. I still call it good research because you have to try 10 bad routes before you find the good one. You can't see these attempts in Python, C++ etc, because they never make it out to the public.

But back to my Teletubbies level of Haskell and CS101.

If you go down the tunnel FP Bros want you in, you'll see that the whole lambda calculus is about rewriting. Graph rewriting systems.

The underlying concept isn't as alien to you, as you'd think!

It's basically refactoring, composability, building programs from LEGO blocks, architectural patterns, design patterns, basically the Holy Grail of Composable Large Systems!

You are playing with a graph, and if you follow rules, it can be tamed:

refactor

If things have controlled behavior and are self-contained, then they can be flung around more easily, and this is why FP Bros keep harping about lambda calculus and pure functions.

This is the tunnel they want you in:

  • Purity,
  • Elegance,
  • Correctness,
  • Composability,
  • with a tiny snort of Math dust.

They don't want you in their real tunnel.

Haskell - just like C or Python (via the interpreter) - is about doing some sort of 0/1 juggling at the end of the day. Yeah, that kind of code (Don't worry, we are just saying hi to Mr. Godbolt!):

godbolt

And that's when you're gonna get one in your Pure Function tac-vest.

We diligently went deep into CS101 Lambda Calculus, but... we went a bit too deep... and we ended up in a place where FP Bros didn't want us.

We went into their real tunnel:

secondary

Finally, after all the graph dangling, thunking, call by name, overflows, mem leaks, !, seq, multiple sublanguages whose surface layer is what people show as Haskell, and of course after Little Boy here comes the point:

You have to sign the paper which says that FP Bros can write pure functions.

In the beginning I said:

"My friend, the only sensible way to live in this world is to live without rules."

If we want rules, we should move to a small town.

It isn't called pure, because it has no side-effects or it is truly a mathematical function. No.

Just think of all the allocs, graph juggling, evaluation and Gödel's nightmarefuel etc...

It is called pure because it isn't some second-grade stuff cooked up by the Gang of Four, or Hell's Angels, or Dev.to articles like mine, but first-grade stuff cooked up your CS 101 Teacher.

The Secret Ending

Oh... I promised you a technique though to defeat FP Bro Elite Players. So, let's see that 999999999 IQ technique.

After all of this talk about lazyness...

How to deal with FP Bros? What is the Secret Ending to Computer Science's PvP?

Sometimes it's best to leave well enough alone

Just?!?!? JUST WALK AWAY? After all of this buildup?!?!?! Bbbbut eventually you'll have to engage FP Bros and then you'll lose!!!!!

Nonono. Technique works. I'll show you!

There was an FP Bro who was taken out by this technique.

Let me introduce you to the high-level lategame Dual-Wielder FP Bro with maxed skill tree and full epic gear. Her name is Alexis King, our primary VIP.

She isn't a SOLID Bro, nor a COBOL Bro. She is a Haskell Bro.

Just peek at her "How to make a Haskell program 5x faster with 16 lines of code" recording, and you'll understand what I mean.

But eventually... this Bro officially took down herself.

In my earlier posts, there was always something cheesy thing we were able to do, to improve the original thing:

With Alexis King though, it is completely and wildly different.

The only thing I can say to Alexis King's work is what our much-much smarter forefathers did in their own time, when they saw something which was dancing at the edge of the Abyss and held immense reality shattering powers: I like it.

And now onto you Dear Reader.

As I clearly stated in the beginning, I can only offer you an "understandment". I showed you a barbaric, toned-down, oversimplified, flawed, academically incorrect, not deep enough, unfaithful misrepresentation of the real thing.

But we made out of the tunnels in one piece, and that's what matters.

We saw some crazy stuff along the way, so now you can say to your JS FP Bro friends that instead of inspecting the bottom:

jslittleboy

They should focus more on what the head tells.

This is the end of our mission, my Imperative Warrior Teammate!

Now you can throw away those nv-goggles and thermals, we won't be needing them anymore.

Of course, throw away that Y-combinator too, because we can't do anything with it in Python:

ypy

But we are for-looping and mutating stuff anyways, so we won't miss it too much.

Come on my Imperative Warrior Friend! You did really well if you survived up to this point! We did what we can, we're leaving this FP place.

Pack it up! We're going home back to Imperative Country!

Just eval those args first in the truck, call func, and grab Z... she goes into witness protection:

zpy

Top comments (0)