Hacker News new | ask | show | jobs
by wokwokwok 1311 days ago
I’ve had similar experiences with scala and clojure professionally. I now actively oppose people attempting to add functional code to projects I work on.

…because when they say “more functional” most people mean:

I want less code.

I want the code to be shorter, because I’m lazy and I want it to be all on one screen.

…but that’s actively harmful to almost any code base.

You want simple code, not dense complicated code. Dense complicated code is for people who wrote the code, and a few smart talented people. Other people have to work on the code too. They cannot.

Actual functional code doesn’t strive for code density, it strives for code purity and algebraic structures.

That’s fine. Do that.

Dense map reduce reduce flow reduce functions can die in a fire.

4 comments

I think you're conflating "readable" and "uncomplicated" with "familiar". I'm equally infuriated by OO code with dependency-injected-everything from some hidden framework configured by fourteen XML files somewhere in a different file tree, interfaces for every single class even if only instantiated once, factories to create builders that make adapters.

Maybe if I stared at it for twelve years it would become familiar and I would begin to think it was simple, readable and maintainable.

Yeah. Sure, "simple" and "complex" sorta have an objective definition. But colloquially "readable", "simple", "complicated", etc have a tendency to track with "things with which I'm familiar/comfortable (or not)".

Over the decades I've come to the conclusion that there's no such thing as a one size fits all sweet spot on this stuff. Different people are going to have different experiences with what they find straightforward or not. They will have different backgrounds, mental models, ways of perceiving the world. It all adds up. As a profession we need to understand this reality and find ways around it instead of getting into dogmatic arguments as if there's One Right Answer.

Common example I give - the GP complained about FP advocates wanting code to take up less screen space. I have come across many devs who struggle with concise code, and many others who struggle when code is not concise. Similarly, I have come across plenty of devs who start having trouble when code is spread out (sometimes that means within a file, across files, both, etc). I have also come across plenty of devs who have trouble when it's all pulled together.

I think this is about the level of abstraction. As React component extraction is to tag soup, so named functions composed are to fp primitives. In code reviews, if I see a big swamp of pipe/fold/cond etc at the top level, I'd kick it back and ask that to be wrapped in a named function that explains what it does, rather than exposing it's guts.

Writing concise, clear code is a skill that straddles any paradigm.

Pretty poor attitude to just adopt so generally. I've seen 'actively harmful' qualities from all paradigms. Once peoples start adopting attitudes like yours they've just become the mirror of the condescending FP type and just kill outright any of the really cool features that are useful, as well as any discussion of them.
Do whatever you want with your own code bases; my responsibility is to make the ones I work on maintainable by other people.

/shrug

Some of us don't find OOP code maintainable.
It doesn’t matter if it’s FP or OOP.

Here’s the real question: do you think dense code is more maintainable?

Generally yes? More than a verbose multilayered abstraction, probably?

…but where do you draw the line? Map statements instead of for loops? Collapse all the white space onto a single 200 character line? Make everything one big regex?

Dense code means that every change has more impact, because there’s less code; it’s unavoidable: less code to do the same work means more impact from changing any part of that code.

That is why it’s difficult to maintain; because you can’t touch it without making side effects; you can’t reason about a small part of the code, because the logic is dense and difficult to unpack into small parts.

Certainly OOP can often be verbose and annoying, but that’s a different thing.

Code density and being functional are orthogonal; some OOP is too dense too. …but generally I’ve found that inexperienced people see density and strive for it, believing this makes it functional; but the truth is the opposite.

Good functional programming is often naturally concise, but most people don’t actually seem to understand FP.

They just seem think it means to put more “reduce” statements in the code, remove for loops and generally make the code harder to debug and denser.

…in my, limited experience, working with lots and lot of different folk at many different organisations.

For me it's not density - it's the OOP class abstractions and all that. I'm not smart enough to keep up with it vs the FP approach of just doing data transformations.
I think of OOP done well at a high level as "structural logic". Whereas in FP, one might use `map()` and `bind()` to replace complex imperative logic flows, in OOP, this is done with object hierarchies and structures.

When you have a abstract base class or an interface that defines some contract, it's creating a "shape" that defines how an instance of the class can be used.

I think that this might be why some folks have an affinity for OOP and some have an affinity for FP. OOP affinity might be tied to more visual thinkers. For me, I see the "shapes" of the code defined by the contracts.

Perhaps the snark caused the down-votes, but your point is legitimate. 'Pure FP' languages encourage code that is nearly unreadable and unparseable without any additional context (and sometimes, unreadable even with said context). There is some strange desperation for extreme terseness in pure FP languages like Haskell, Ocaml, Idris, etc.

Single-character variables and functions, point-free style... Coming from an imperative background, this just seems like flexing for the sake of flexing.

Why not make life a little easier by clearly naming variables? This isn't maths or physics where variables (for some equally-inane reason) must be one character. We have 4K screens today; I can accept lines that are significantly longer than 80 chars.

When your code is sufficiently abstract, there often really aren't better variable names than a or x. My experience is that it's about the scope for that variable. If it's in a one-line lambda, then it'll be one letter. If it is going to be used in the next 10 lines or so, make an abbreviator. And it's longer, or particular unclear, spell it all out. Adding extra words don't make BusinessAbstractFactoryIBuilder more readable.
> BusinessAbstractFactoryIBuilder

While I understand and agree with this meme[1], I think that's the other extreme, where everything is a Factory Builder thing.

Even so, I would rather too much information than too little, which is what FP programs tend to do. Over-abstraction is also a problem, in my view. Even in a LINQ lambda, for instance, I might write

  someEnumerable.Select(what_is_actually_inside => doSomething(what_is_actually_inside))
rather than

  someEnumerable.Select(x => doSomething(x))
[1]: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
Funny example since currying requires no intermediate variable to speak of

    someEnumerable |> select doSomething
even funnier when you realize that doesn't even need to be curried so it also works in c#:

    someEnumerable.Select(doSomething)
to be fair, i guess doSomething was supposed to be an actual function body instead of a helper function from somewhere else
Fwiw, OCaml doesn't chase extreme terseness or point-free programming. It's not really equipped for that.

OCaml is designed for straightforward code centered on functions, modules, and a mix of imperative and immutable data structures; all with a _really_ simple execution model and a pretty nice type system. The core language has very few quirks and it's easy to predict what it does, and how efficiently.

There's not really any comparison with Haskell, which emphasizes heavy optimizations to make its terseness, based on lazy evaluation, work well in practice.

There's nothing wrong with single character variables if you're not using them like a complete idiot. A line like reports.map(r => whatever) makes it blatantly obvious that r is a report.
there is zero desperation for extreme terseness in ocaml.

some very obvious examples:

- many, if not most, functions have sensible names instead of abnormally terse ones

- it's possible to make named parameters mandatory, and many do that - e.g. the base library