Dressing a pure function as an object sounds a bit OOP cargo cultish, like promoting weird service "objects" at all costs instead of domain objects that would actually behave like objects (e.g. a ReserveStuffService instead of a StuffReservation).
My solution in this case is similar. Instead of a PayrollCalculator returning a PayrollResult value-object I'd rather have an immutable CalculatedPayroll or PayrollCalculation or something that lazy-calculates and memoizes the necessary values internally, exposing only the taxes, debits etc. variables, and maybe a single method called "precalculate" that would trigger the process eagerly.
I could do new kinds of calculations (California, New York) with simple inheritance: CaliforniaPayrollCalculation, overriding some of the private methods.
Is there a problem with my implementation that will bite me in the ass later?
There are lots of reasons to choose an imperative language and write functional code in it. Ecosystem, familiarity, quality of documentation, likelihood of finding helpful solutions on the web, hiring pool. I could go on.
I spent a few years working in Clojure, and I’m glad I did. It taught me to think in FP. Now I use those same FP techniques in a language I vastly prefer overall (TypeScript).
There’s really very little reason not to write functional or mostly-functional code in any language.
Why would you prefer Typescript over, say, Clojurescript if functional is your priority? It's the immutability that matters in functional. Typescript is just a better way to manage mutable code.
> Why would you prefer Typescript over, say, Clojurescript if functional is your priority?
Because the benefits of a powerful static type system (type checking, documentation, refactoring) are also a priority.
I’ve used ClojureScript extensively, and shipped cljs projects in production. But one of the problems I encountered frequently was completely inexplicable behavior when I mistakenly passed incompatible values to core functions. Absolutely bonkers errors or silent failures I had to track deep into the inner recesses of cljs.core etc, generally having to reverse engineer the advanced compilation to understand what had happened.
When I filed bugs on cases like this, eg asking for a meaningful runtime error, they were without exception closed as WONTFIX with snooty notes like “just don’t pass incompatible values to core functions”.
While I’m quite a fan of TDD, even my extensive coverage would sometimes fail to catch issues like that. But TypeScript routinely catches them for me.
> It's the immutability that matters in functional.
I believe the ML family of languages would strongly disagree...
> Typescript is just a better way to manage mutable code.
TypeScript is a better way to catch type errors in JavaScript, mutable and immutable alike. But it does help enforce immutability if you use `readonly`/`as const` extensively, which I do.
Sure, I’d vastly prefer immutability was the default. But it’s a small price to pay for such huge productivity and safety improvements.
My solution in this case is similar. Instead of a PayrollCalculator returning a PayrollResult value-object I'd rather have an immutable CalculatedPayroll or PayrollCalculation or something that lazy-calculates and memoizes the necessary values internally, exposing only the taxes, debits etc. variables, and maybe a single method called "precalculate" that would trigger the process eagerly.
I could do new kinds of calculations (California, New York) with simple inheritance: CaliforniaPayrollCalculation, overriding some of the private methods.
Is there a problem with my implementation that will bite me in the ass later?