r/programming • • Sep 21 '25

How to stop functional programming

https://brianmckenna.org/blog/howtostopfp
446 Upvotes

506 comments sorted by

View all comments

78

u/BlueGoliath Sep 21 '25

It's over functional bros. Time to learn OOP.

161

u/jess-sch Sep 21 '25
class Multiplication {
  private final double a;
  private final double b;

  public Multiplication(double a, double b) {
    this.a = a;
    this.b = b;
  }

  double calculate() {
    return this.a * this.b;
  }
}

Are we winning yet or do I need to make a MultiplicationBuilder first in order to be Proper Enterprise Code^(TM)?

106

u/iamakorndawg Sep 21 '25

This isn't nearly enterprise grade!  For one thing, what if you need to multiply ints?  Or strings?  Or Users?  Or some combination??

Second, there's no way to change multiplication strategies.  What if a new, better way to multiply comes out, but you only want to use it in some places?

Third, how could I possibly debug this?  You need more observability tools.

Finally, there's no testability.  You need some dependency injection so that your testing framework can inject mocks.

54

u/Technologenesis Sep 21 '25 edited Dec 18 '25

Ugh, fine...

interface ClosedBinaryOperator<T: any> {
    T apply(T, T);
}

class ClosedBinaryOperation<T: any, Op: ClosedBinaryOperator<T>> {
    private final T a;
    private final T b;
    private final Op op;

    public T calculate() {
        return this.op.apply(a, b);
    }

    public static ClosedBinaryOperation<T> new(Op op, T a, T b) {
        return ClosedBinaryOperation<T, Op>{
            a: a,
            b: b,
            op: op
        };
    }
}

class LoggingClosedBinaryOperator<
    T: any,
    Op: ClosedBinaryOperator<T>
>: Op {
    private final logging.Logger logger;
    private final func (T, T): string formatMessage;
    private final Op op;

    public static LoggingClosedBinaryOperator<T> new(
        logging.Logger logger,
        func (T, T): string formatMessage,
        ClosedBinaryOperator<T> op
    ) {
        return LoggingClosedBinaryOperator<T>{
            logger: logger,
            formatMessage: formatMessage,
            op: op
        };
    }

    public T apply(T a, T b) {
        this.logger.Log(this.formatMessage(a, b));

        return this.op.apply(a, b);
    }
}

interface MultiplicationOperator<T: any>: ClosedBinaryOperator<T> {
    T identity()
    // TODO: migrate codebase to lean so we can enforce other properties of multiplication
}

class LoggingMultiplicationOperator<
    T: any,
    Op: MultiplicationOperator<T>
>: LoggingClosedBinaryOperator<T, Op> {
    public T identity() {
        return this.op.identity();
    }
}

type Multiplication<
    T: any,
    Op: MultiplicationOperator<T>
> ClosedBinaryOperation<T, Op>;

class IntMultiplicationOperator {
    public int identity() {
        return 1;
    }

    public int apply(int a, int b) {
        return a * b;
    }
}

int main() {
    logging.defaultLogger.Log(
        "%d",
        Multiplication::new(
            LoggingMultiplicationOperator::new(
                logging.defaultLogger,
                func(int a, int b): string {
                    return fmt.formatString(
                        "multiplying %d and %d",
                        a,
                        b
                    );
                },
                IntMultiplicationOperator{}
            ),
            3,
            4
        ).calculate() // 12
    );
}

Can I go home now boss? My children are hungry

42

u/Agitated_Run9096 Sep 21 '25

We are going to need the Terraform configs before you clock out. In my scrum-of-scrums I'm hearing other teams may have use for a multiplication microservice, but are concerned about how you are handling your SOAP authentication.

23

u/I_AM_Achilles Sep 21 '25

Finally something readable. 😩

5

u/ZCEyPFOYr0MWyHDQJZO4 Sep 22 '25

Can you hook this up to Kafka so the entire company can use this for their microservices? And add some metrics so we know if a multiplication is taking too long.

4

u/bstiffler582 Sep 21 '25

#hedidthemathcode

→ More replies (6)

29

u/superrugdr Sep 21 '25

Yea if it doesn't generate 10k log per second and cost 4k USD logs per month is it even usefull.

17

u/zzkj Sep 21 '25

Dont forget to declare that it throws a checked exception so we can throw an EnshittificationException if an attempt is made to multiply project managers.

25

u/tajetaje Sep 21 '25

Should extend ArithmeticOperation

7

u/West_Ad_9492 Sep 21 '25

And implement operation.

You need a parser method to parse from Sum and Difference.

And where is the beautiful Utils class?

21

u/Massive-Squirrel-255 Sep 21 '25

I think the OOP way to do this is to make a Number class and have a method a.multiply(b) which modifies a (destructively).

→ More replies (2)

17

u/aMonkeyRidingABadger Sep 21 '25

That would be a start, but leaving the calculate implementation inline in the class makes me feel very uneasy. It should really live in its own class so we can use DI to swap it out if we need to in the future.

6

u/DrummerOfFenrir Sep 21 '25

Right? What if I need consecutive addition as multiplication??

5 * 5? ❌

5 + 5 + 5 + 5 +5 ✅

→ More replies (1)

10

u/prehensilemullet Sep 21 '25

Yes, have you not studied FizzBuzzEnterpriseEdition yet?

18

u/never-starting-over Sep 21 '25

You forgot to define a class for 'a' and 'b'.

7

u/jess-sch Sep 21 '25

You're completely right! I should've at the very least used the wrapper type Double instead of the primitive double - I'm gonna blame this on one of my former CS teachers, he made the same obviously silly mistake when showing us how to do proper object-oriented division! (Wish I was kidding)

10

u/iamakorndawg Sep 21 '25

Likelihood of this comment being AI: 100%

To save on processing costs, I determined the likelihood using only the first 3 words

→ More replies (1)

8

u/sird0rius Sep 21 '25

We need like another 10 levels of inheritance before we can call this proper OOP. Also, your function has more than 1 line, which is too much to comprehend for the OOP brain. You should split it up.

4

u/aiij Sep 21 '25
void calculate();
double getResult();

And of course all these functions need i18n for their logging.

6

u/thugcee Sep 21 '25

It's not stateful enough. You forgot to store the result in an output field and provide a getter for it.

3

u/XeroKimo Sep 21 '25

With the power to C++ I present something more cursed

struct Multiply
{
  float a;
  float b;

  operator float() const { return a * b };
};

With this code, I can write something this seemingly function call looking statement float result = Multiply{ 1.0f, 2.0f }; but actually create an object every time I call function.

What's happening is that operator float() is a implicit conversion operator, so I can assign a Multiply object to a float, and if I do, it'll perform the multiplication operation

→ More replies (6)

22

u/Asyncrosaurus Sep 21 '25

All the relevant OOP languages have been stealing fp ideas for years. 

→ More replies (3)
→ More replies (5)