How to stop functional programming (2016)

(brianmckenna.org)

105 points | by thunderbong a day ago ago

114 comments

  • astrobe_ a day ago

    Beyond the satire, one is supposed to write code which is readable, and like often with written works, one has to think about the "audience", the readers. When you write technical documentation, you have to decide ahead of time the expected skill level of the reader - often that decision is written too in the intro as "prerequisite(s)".

    When writing code you have the motto "don't make me think" in mind, but how to know what's the maximum level of trickiness for readers? There are familiar techniques and idioms when it is your main programming language, but they are not for someone using this language on the side.

    In any case, neither code nor comments should be tutorials. To a reasonable extent, it is up to the reviewer to do their homework or just ask. Then based on that interaction you can add a comment or a parenthesis, or uncompress a bit the code. But not to the point that it means to "dumb down" things, because it is a downward spiral.

    • dimal a day ago

      > When writing code you have the motto "don't make me think" in mind

      I disagree with this phrasing. We’re engineering after all. The entire job is thinking. If someone doesn’t want to think, then they shouldn’t be a programmer.

      Readability matters, though. I try to have a narrative structure in my code, so it leads the reader along. Formatting matters. And documentation helps. If you want to introduce an unfamiliar idiom that might be more functional, good, but document it. Share it with the team and talk about it. I know that writing and reading documentation is usually seen as a waste of time unless you’re doing it for AI, but I’ve seen it work well in multiple teams. In my experience, the teams with poor docs have the worst code.

      • scubbo a day ago

        > > When writing code you have the motto "don't make me think" in mind

        > I disagree with this phrasing. We’re engineering after all. The entire job is thinking.

        Well, sure. The implied full phrase - more technically-correct, but less pithily-quotable - would be something like "don't make me think unnecessarily; let me spend my thoughts productively. If you've already spent brainpower on figuring something out, explain it to me directly and clearly rather than forcing me to go through the same discovery process"

        So - yes, if someone doesn't want to think _at all_, they shouldn't be a programmer; but if someone has an aversion to being forced to solve a problem that someone else has already solved, they likely have the right "shoulders-of-giants" mindset.

        (For any potential pedants - yes, there are some practices you simply have to work through before understanding dawns, which cannot be explained directly. Still, though - the explanation should aim to minimize unnecessary thought-requirements so the student can get straight to learning)

        • about3fitty 7 hours ago

          To add to this, engineers consider tradeoffs.

          You might choose to add comments and let the logic unfold in a less succinct way in order to improve readability and understandability.

          You might also consider your colleagues’ limited cognitive reserves, some of which could be spent on more important issues.

      • wk_end a day ago

        Just because fires are inevitable in the business of firefighting, doesn’t mean that firefighters are in the business of starting fires.

        In engineering, thinking is inevitable but also costly and error-prone, and the more thinking an engineer needs to do the higher the costs, the higher the risks of errors. We should be striving to minimize the thinking required every step of the way, even if we’ll never get it down to zero; that’s the only way to keep it from spiraling towards infinity.

      • politelemon a day ago

        Teams are not a static concept, they change members over time and so talking to them has a net zero effect. The newer members will not understand this clever new concept that was introduced, worse, they will misunderstand it or work around it. Which brings us back to don't make me think. If the concepts can be kept as simple as possible, which overlaps greatly with readability, it will have achieved its purpose.

      • odyssey7 a day ago

        "I didn’t have time to write you a short letter, so I wrote you a long one." -- Mark Twain

        It's difficult to explain how to write well, but bad writing and bad computing systems typically impose far greater cognitive burden on readers than might have been necessary. There is an art to software engineering.

        Functional programming does involve idioms, though I would say no more than imperative programming, or OOP, or some other paradigm. One of the overarching themes of FP is to reduce the cognitive footprint to only the essential properties of the problem to be solved.

        A novice artist can produce a recognizable figure using many pencil strokes. It takes a master to produce a compelling likeness with only the lines that are necessary.

      • MrJohz a day ago

        Maybe a better phrasing is "don't make me think about inconsequential details". If I need to figure out your particular idiom of writing a for-loop, then that takes time away from thinking about what the code is actually doing. Even if that idiom is perfectly fine and works the same as any other for loop, it's not worth it if it makes people think about unnecessary things.

        But like you say, if you've got an idiom that's actually useful (like programming in a functional style to avoid action-at-a-distance side effects), then document it so the whole team can get used to it and stop having to think about it.

      • nerpderp82 19 hours ago

        When I write functional code I start with Peano and then go from there, ommiting comments are descriptive names. It encourages thinking in myself and my coworkers.

    • Archelaos 20 hours ago

      > neither code nor comments should be tutorials

      Why not? To take the example from the article:

        def userCoworkers(u: User): List[Employee] =
          u.departments.flatMap(_.employees) 
      
      Why not add a comment like:

        // Automatically iterates through all the user's
        // departments and assembles a single list of all
        // their employees.
      
      Perhaps a note should also be added explaining why the domain model opted for u.departments and not for something more natural like u.company.departments. And the comment should also explain, why we do not filter for duplicates here.

      I insert many such comments into my own code when I use a rare language feature or a somewhat peculiar element of the domain model. This makes refactoring my own code much easier, faster and more bug-resistant. When composing code, it is generally simple and quick to add such a comment, as it is merely a matter of writing down the result of my immediate thought process.

      • AdieuToLogic 14 hours ago

        IMHO, comments are required for capturing "the why." Code captures how it works, when it is used, and what it does.

        But only the author(s) can capture why it exists via commenting.

      • Xss3 8 hours ago

        Id argue the function name and placement are bad. I have coworkers that arent in my department. If its not obvious what the method is doing from the name and requires a comment then its a bad name.

      • bn-l 19 hours ago

        Same. And it’s helpful not just for other people but for me also when I’ve forgotten how it works and now there’s no one to ask.

    • firesteelrain a day ago

      > one has to think about the "audience"

      This 100%

      When I am doing MBSE, and I discuss what level of detail is required in my abstract models one of my senior coworkers would remind me to “know your audience”. It has helped me and I use the same phrasing when I am coaching my teams now.

    • sdeframond a day ago

      I don't know.

      On the one hand, this stance seems common sense: why overcomplicate things? Let's just do what's commonly understood and move on.

      On the other hand, writing code that the most junior in your team can understand leads to mediocre code. In the end, it crumbles under its own weight.

      Most of what we master now was difficult to understand first. Should abandon generics because "it's difficult" ? Should we ditch static typing because "we're not used to it" ?

      Maybe.

      Or, maybe, this is the right tool that can take us to the next step. So we pause and think at this new approach to understand it then master it.

      • darepublic a day ago

        I don't really believe this but ok. I worked at a functional / typescript heavy shop where when I had parsed what a complicated set of steps was doing discovered multiple redundant queries and a library that was parsing strings into code. But damn some of those functional paradigms chef kiss /s. Imo my dumbass code , besides being demonstrably more performance was way less of a liability.

        • sdeframond 21 hours ago

          Sure, replace functional/generics/static typing with your poison of choice.

          Just remember that, at some point, the "dumbass" thing was pushing sticks into clay tablets.

          Functional programming may not be the future. But dismissing other's code because it is not done "the way we are used to" is stupid.

        • yobbo 13 hours ago

          Your argument is "I once saw someone do stupid FP, and it is all I have seen. Therefore, I believe all FP is stupid."

          • darepublic 31 minutes ago

            And this article is - Oh so you don't like FP do you? Have all the side effects in the world! You don't worship at the church of monad? I guess no "pure" functions for you then! bwa hahah

        • BoiledCabbage 17 hours ago

          > I worked at a functional / typescript heavy shop where when I had parsed what a complicated set of steps was doing discovered multiple redundant queries and a library that was parsing strings into code. But damn some of those functional paradigms chef kiss /s

          And I've worked at a place where the imperative code was crap. Should we abandon all imperative code because I had that experience?

          People can write bad code in any paradigm - that's not what decides how we should code.

          Nobody says we should throw away all imperative code because of one poorly implemented code base. But for some reason I see that targeted at FP all the time.

      • jjav 19 hours ago

        > On the other hand, writing code that the most junior in your team can understand leads to mediocre code.

        Disagree on a couple levels. First, while the overall architecture of the system might be complex, there shouldn't be any one single function that isn't readily readable by a junior new hire. Complexity is in the system, there should be no complexity in small sections of code.

        Second, the ultimate level of mastery is to be able to build simplicity. Any newbie can write a convoluted mess that nobody can understand.

      • cyberpunk 20 hours ago

        > On the other hand, writing code that the most junior in your team can understand leads to mediocre code.

        Sorry but this is absolutely false in almost all regular corporate software jobs.

        As i get greyer, I find myself spending much more time planning before I write any code now; what I would previously have run into headfirst and solved with a bunch of advanced language features and complicated logic usually now ends up being implemented in much less and simpler code.

        This is the sign of a senior developer, in my book.

        Writing code anyone finds hard to understand is a marker of inexperience to me.

        • sdeframond 19 hours ago

          If the problem can be solved efficiently with simple, write simple code by all means. Heck, if it can be solved with a spreadsheet, please use a spreadsheet. Even your boss can read it.

          But, at some point, spreadsheets crumble and become unmaintainable. And so does "basic" code.

          Should people keep on using spreadsheets ? Depends.

          • Xss3 9 hours ago

            Absolutely not true.

            Case in point, a ledger, like a spreadsheet but simpler, hasnt 'crumbled' in a hundred years despite being as basic as it gets.

            Simple is good when it's fit for purpose.

            Sounds like your idea of simple is just bad code that is unfit for its purpose. Theyre not the same thing.

            • sdeframond 8 hours ago

              > a ledger, like a spreadsheet but simpler, hasnt 'crumbled' in a hundred years despite being as basic as it gets.

              Yet, for many reasons, your bank does not run your account with a spreadsheet.

              > Sounds like your idea of simple is just bad code that is unfit for its purpose

              Not quite. Simple is good. Do simple. But some things cannot be achieved with familiar tools. Sometime it is simpler to use an abstraction that is difficult to grasp, initially, than to write piles of code "the way we are used to" because we are afraid of "making others think".

              Please make me think, but only if it is worth it.

              • Xss3 3 hours ago

                The problem has nothing to do with the code being simple, but with it being unfit for purpose. You're conflating two separate issues.

    • OhMeadhbh 20 hours ago

      Fuck that. Software engineers should make an effort to learn their craft. Or even engineering methodology.

    • LAC-Tech 20 hours ago

      If you're a computer programmer, coding in a high level language with closures and garbage collections, and you are confused by flatMap: I think you should find another career.

      We need to have a basic level of competency.

      • Spivak 19 hours ago

        I don't think anyone is confused by flatMap in isolation. It's chains of not-very-obvious data transformations. When I have to do one of these I typically break out each step into it's own line/block and add comments with an example object to show precisely what every intermediate step looks like. They're hard to follow otherwise.

        And functional programming snorts these long chains like lines of coke and when you add some random curried functions and people who write in a pointless^H point-free style it's easier than folks like to admit it to end up with write-only code.

        I've never found myself with a block of code that could be expressed as a flatMap and thought it would be easier to understand by expressing it as such. I'm perfectly capable of writing functional code but after reading it back I almost always throw it out for something imperative because it's clearer as to what's really going on. The code is going to be executed in an order at the end of the day, why make it harder to see what it will be?

  • jmmv a day ago

    Missed chance to do something like:

      class User:
        def calculateCoworkers() = {
          this.coworkers.clear()
          for { d <- this.departments }
            this.coworkers ++ d.employees
        }
    
    and then, somewhere else...

      user.calculateCoworkers()
      ... many lines after ...
      for { c <- user.coworkers }
        ... do something ...
    
    Yes, I've seen code like this many, many, many times where class members are used as "global variables" to pass state across functions. And I've noticed AI likes to generate code like this too (possibly because of the former (large presence of this "pattern" in the training data), which means I'm encountering this now in pull requests...
    • sevensor 18 hours ago

      Bonus points if you do half the calculation in the parent class and the other half in a child class. Double bonus points if one or both of them also hits a database.

  • skybrian a day ago

    Apparently, someone doesn’t really get what makes functional programming hard to understand? It’s not writing the occasional pure function.

    Code review or pair programming might help here, to learn the team’s common idioms.

    • IshKebab a day ago

      Yeah this 100%. FP isn't hard because of FP - it's all the other things that popular FP languages come with that makes them hard to understand. Arguably most of them are not related to FP; they're just style choices:

      1. Global type inference.

      2. Implicit syntax (no brackets for function calls, commas to separate arguments, semicolons to end statements/expressions, etc.)

      3. Currying & point free style.

      4. Tendency to have very deep nested expressions. The gap between `let foo =` and it's actual value can often be hundreds of lines.

      I'm sure you can write FP code that avoids these issues and is easy to follow but it doesn't seem like people do that in practice.

      Rust avoided all of these issues fortunately.

      (Oh I forgot about monads.)

      • amluto a day ago

        Also: avoidance of mutable state, even locally. A lot of functions can be more straightforwardly expressed using mutable variables and data structures, and most functional languages can handle local mutable state, but a lot of code in functional languages avoids it.

        Conversely, a lot of code written in imperative languages would be clearer and/or less bug-prone if it avoided mutable state and used persistent data structures.

        I wish there was a mainstream, high performance language that made both styles equally ergonomic.

        • calebh a day ago

          In my experience the main benefit of functional programming is function purity. I am completely fine with mutation inside of a function since the all of the mutation logic is self-contained in a single small block of text.

          I think everyone should take a shot at writing a non-trivial functional program to see the benefit. Once you understand what makes it great, you can apply what you've learned to the majority of OOP/impure languages.

        • brabel 21 hours ago

          Flix has "regions", which are a way to allow mutation locally while keeping the language purely functional otherwise: See Region-based Local Mutation in https://flix.dev/

        • Philpax a day ago

          Does Rust not meet that description?

          • amluto a day ago

            Rust is very good at making things immutable. But that doesn't mean it's particularly fun to, for example, append or prepend something to a list and retain a reference to both the old and the new list.

            Compare to most "functional" languages, in which prepending an item to a list and ending up with immutable references to the old and new lists is almost the defining feature of the language.

            • Mond_ a day ago

              Considering the performance implications of a full commitment to immutable data structures, I think Rust does a pretty good job here.

              Dynamic arrays / vectors / slices or whatever you want to call them are probably the most important or fundamental non-plain-old-data data structure, and in a purely immutable environment these are essentially impossible (or have awful performance characteristics).

            • IshKebab a day ago

              Yeah although I find FP languages' obsession with singly linked lists to be a real flaw because they have such awful performance characteristics. And also pretty bad ergonomics often. The number of times stuff comes out backwards...

        • a day ago
          [deleted]
        • delta_p_delta_x a day ago

          > I wish there was a mainstream, high performance language that made both styles equally ergonomic.

          Unironically, C++.

          • Mond_ a day ago

            > > I wish there was a mainstream, high performance language that made both styles equally ergonomic.

            > Unironically, C++.

            At best C++ falls under "equally unergonomic".

          • ndriscoll a day ago

            Or depending on what "high performance" means (e.g. if Java or Go would be considered acceptable), Scala.

      • elevation a day ago

        > I forgot about monads

        I've been solving business problems with code for decades. I love pure, composable functions, they make my job easier. So do list comprehensions, and sometimes, map and filter. Currying makes sense.

        But for the life of me, no forum post or FP tutorial that I could find explained monads in clear language. I've googled "what is a monad" once a year, only to get the vague idea that you need monads to handle IO.

        I wondered if my brain was broken, but now I'm wondering if most FP adherents are simply ineffective communicators: they've got an idea in their head but can't/won't express it in a way that others after them can understand. In other words, the exact same reason why TFAuthor was corrected by his employer.

        • ekidd a day ago

          So, one way to understand a monad is that it's essentially a container type with "map" and "flatten" operations. Let's say your monad is type M<T>. The "map" operation allows you to take a function T->U and a container M<T>, and transform each element, giving you a container M<U>.

          "Flatten" takes a container of type M<M<T>> and returns a container of type M<T>. So a List<List<Int>> becomes a List<Int>.

          Now comes the trick: combine "map" and "flatten" to get "flatMap". So if you have a M<T> and a function T->M<U>, you use "map" to get an M<M<U>> and "flatten" to get an M<U>.

          So why is this useful? Well, it lets you run computations which return all their values wrapped in weird "container" types. For example, if "M" is "Promise", then you can take a Promise<T> and an async function T->Promise<U>, and use flatMap to get a Promise<U>.

          M could also be "Result", which gets you Rust-style error handling, or "Optional", which allows you to represent computations that might fail at each step (like in languages that support things like "value?.a?.b?.c"), or a list (which gets you a language where each function returns many different possible results, so basically Python list comprehensions), or a whole bunch of other things.

          So: Monads are basically any kind of weird container that supports "flatMap", and they allow you to support a whole family of things that look like "Promise<T>" and async functions, all using the same framework.

          Should you need to know this in most production code? Probably not! But if you're trying to implement something fancy that "works a bit like promises, or a bit like Python comprehensions, or maybe a bit like Rust error handling", then "weird containers with flatMap" is a very powerful starting point.

          (Actual monads technically need a bit more than just flatMap, including the ability to turn a basic T into a Promise<T>, and a bunch of consistency rules.)

          • Veedrac 11 hours ago

            > and they allow you to support a whole family of things that look like "Promise<T>" and async functions, all using the same framework.

            You've highlighted here the part that would actually explain the purpose of a monad, but not explained it. You don't need a monad abstraction to have things with monadic properties, and indeed often reality isn't quite perfectly shaped to theory, so forcing your object to fit the abstraction can be costly. One very obvious cost is that you no longer get descriptive names of what the monadic bind does; you have to infer it from what you know of the type.

            The one thing a monad abstraction definitely gives you is the ability to write code that's generic over all monads. This is weird because this almost never happens outside of strongly functional languages.

            If you'll forgive linking to a decade-old Reddit post, I've talked about this before.

            https://reddit.com/r/programming/comments/4t6a6q/functional_...

            (also, parent for context: https://reddit.com/r/programming/comments/4t6a6q/comment/d5f...)

            • ekidd 4 hours ago

              > You don't need a monad abstraction to have things with monadic properties, and indeed often reality isn't quite perfectly shaped to theory, so forcing your object to fit the abstraction can be costly.

              Please note that I was trying to explain what a monad is, to somebody who wanted to understand. I also suggested that people writing typical code shouldn't actually need to know this in order to do their jobs:

              > Should you need to know this in most production code? Probably not! But if you're trying to implement something fancy that "works a bit like promises, or a bit like Python comprehensions, or maybe a bit like Rust error handling", then "weird containers with flatMap" is a very powerful starting point.

              Monads are an incredibly stripped-down mathematical structure ("container-like things that support flatMap"). And as such, people who design certain types of programming languages or libraries may benefit from being aware of monads. At least for languages with closures. Surprisingly few languages can actually support monads as a first-class abstraction in the language, because to make first-class monads nice you need a certain kind of type system. Which often isn't worth it.

              Where I probably differ from your opinion is that I think implementing "almost monads" like JavaScript promises is very often a mistake. The few places where JavaScript promises break the monad laws are almost all nasty edge cases and obscure traps for the unwary. Similarly, if you implement list comprehensions that break the monad laws, most likely you just get awful list comprehensions.

              There are exceptions. Rust has a lot a "almost monads", but this is mostly because Rust function types are a mess thanks to the zoo of Fn, FnOnce and FnMut. Rust would be a simpler and easier-to-learn language if Future<...> actually followed the monad laws. But in this case, it sadly wasn't possible, and I would argue that Rust is worse for having so many "almost monads."

              This may all make more sense if you knew my tastes in programming languages, which is "languages where all the parts fit together cleanly with no surprising edge cases that prevent 'obvious' things from working." One way you can accomplish this is to have some kind of simple mathematical structure underlying your language, and to avoid adding dozens of features that almost follow clean rules. C++ never took this approach, and so C++ library designers need to be aware of all sorts of interactions between weird corner cases.

              So another way of summarizing my argument is "If you have list comprehensions that somehow don't follow the monad laws, then you're going to confuse users and permanently add technical debt to your language. Make sure it's actually worth it."

          • IshKebab a day ago

            I think this might be the clearest explanation I've seen. Nice work!

        • bazoom42 11 hours ago

          The association between IO and monads is just that Haskell uses a monad for IO as a clever hack to support IO in an otherwise pure and lazy language. But this is just one use of monads. Monads in themselves does not have anything in particular to do with IO.

          Monads are a pattern for function composition. In most languages it is verbose compared to alternatives (like map and filter), but Haskell have some syntactic sugar which makes it a nice succint way of chaining functions.

          The problem with monad tutorials is they try to explain monads as some abstract, universal concept disconnected from practical code. But try to explain functions or classes the same way and it would also be incomprehensible for someone unfamiliar with the concept.

          It is not due to ill will, it is just that Hakell-fans tend to have a background in mathematics and therefore default to the most abstract explanation possible, where software developers prefer to start with something practically useful and then generalize from there.

          You probably wouldnt understand ‘map’ either, if it was explained to you by a haskelite, since they tend to explain functions in terms of their type signatures rather than what they actually does.

          Monads should be exlained through practical code examples in Haskell since this is the context where it makes sense.

        • AdieuToLogic 13 hours ago

          > But for the life of me, no forum post or FP tutorial that I could find explained monads in clear language. I've googled "what is a monad" once a year, only to get the vague idea that you need monads to handle IO.

          Another phrase to search for is "railway oriented programming"[0], which technically describes a monad called a Kleisli[1]. Still, there are many introductions in a lot of languages based on the term above.

          HTH

          EDIT:

          Something to keep in mind is that the term "monad" identifies a mathematical concept having well-defined behaviors, not a specific construct. So searching for "what is a monad" is akin to searching for "what is a happiness."

          0 - https://medium.com/geekculture/better-architecture-with-rail...

          1 - https://bartoszmilewski.com/2014/12/23/kleisli-categories/

        • Asraelite a day ago

          https://tech.nextroll.com/blog/dev/2022/11/11/exploring-mona...

          The thing that always makes FP concepts click for me is seeing them explained in a language that isn't Haskell or similar.

          I don't know why people are so obsessed with using Haskell for tutorials. Its syntax is a nightmare for newcomers and you can do FP in so many other languages.

          • mrkeen a day ago

            For instance, C#:

              public static System.Collections.Generic.IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,System.Collections.Generic.IEnumerable<TCollection>> collectionSelector, Func<TSource,TCollection,TResult> resultSelector);
            
            compared to:

              (>>=) :: m a -> (a -> m b) -> m b
            
            Guess which one I googled, and which one I typed from memory.
            • mrsmrtss 21 hours ago

              In your c# sample full namespaces are unnecessary and will add only noise in this context.

            • 21 hours ago
              [deleted]
          • bazoom42 7 hours ago

            Haskell have some syntactic sugar which makes monads nice to use, which is why monads are popular in Haskell.

            Explaining monads in JavaScript or C# might show the mechanics but will not show why anyone would actually want to use them, since it just result in overly convoluted code.

            • Asraelite 6 hours ago

              I strongly disagree. Monads are used all the time in non-FP languages. Parser combinators are one common example. It's just a programming pattern which gives the benefits of global variables without the downsides. They work perfectly fine without dedicated syntax.

              I was very confused about what they were until I saw an article similar to the one I linked, and then I realized that I had actually been using monads all along, I just didn't know they were called that. I think a lot of developers are in the same boat.

              • bazoom42 4 hours ago

                I dispute that monads are used “all the time” in non-FP languages. Can you provide some examples?

        • shakadak 18 hours ago

          You've mentioned list comprehensions, map, and filter, so I suppose you mostly used these concepts with lists/arrays.

          One question you could ask yourself is, how could you reproduce list comprehensions without special syntax ?

          Another way to view monads is by following the types. With map, you can chain pure functions from one type to another to be applied on lists (hence the (in -> out) -> ([in] -> [out]) ) . How would you do that chaining with function from one type to another but wrapped in a list ( (in -> [out]) -> ([in] -> [out]) ) ?

          Then you can think about how it could be applied to other types than lists, for example, nullable/option types, result types, async/promise types, and more hairy types that implement state, reading from an environment, etc...

        • a day ago
          [deleted]
        • OhMeadhbh 20 hours ago
        • vjerancrnjak a day ago

          I felt similar with lenses. The problem lens solve is horrible. You don’t even want that problem.

          FP can be the pragmatic as well. You’re going to glue up monad transformers, use lenses like there’s no runtime cost, and compute whatever you need in days but at least you know it works. Maybe there’s accidentally quadratic behavior in lifting or lenses but that’s by design. The goal is to just throw software at things as fast as possible as correctly as possible.

          • AdieuToLogic 13 hours ago

            > I felt similar with lenses. The problem lens solve is horrible. You don’t even want that problem.

            Lenses abstract properties in a composable manner. How is this problem horrible?

            > FP can be the pragmatic as well. You’re going to glue up monad transformers, use lenses like there’s no runtime cost, and compute whatever you need in days but at least you know it works. Maybe there’s accidentally quadratic behavior in lifting or lenses but that’s by design. The goal is to just throw software at things as fast as possible as correctly as possible.

            Any abstraction can be used inappropriately. Slavish adherence to an approach in spite of empirical evidence is a statement about those making decisions, not the approach itself.

            In other words:

              A poor craftsman blames his tools.
            • vjerancrnjak 10 hours ago

              Lenses: solve a problem elegantly (if you can hide the boilerplate) but inefficiently. A self caused problem by having extremely nested records. How did you get to a point where you have a structure that's hard to play with?

              Lenses are exactly glue for throwing software at things as fast as possible as correctly as possible. A poor tool.

              The very need for lenses often indicates that the data model has been designed in a way that's hostile to direct, ergonomic manipulation. A glued up steampunk contraption, a side-effect of throwing software at everything as fast as possible. Invented in a language environment where they can't be efficient.

              Monad transformers: lift has quadratic complexity and runtime cost. Not really composable. Similar to effect systems in other languages, control flow becomes very unclear, depending on the order of application.

              Lenses and monad transformers are just a nice trick that you shouldn't ever learn.

              But I agree with your last statement, many of these libraries are just poor craftsmans giving us new tools that they made for problems we never want to have.

              It's similar to dependency injection, why would anyone need a topological sort over dependencies and an automatic construction of these dependencies? Is it so hard to invoke functions in the right sequence? Sounds like you've made a program with too many functions and too many arguments. (or in oop, too many classes with too much nesting and too many constructor args)

              These tools are "pragmatic". Given the mess that will naturally arise due to poor craftsmanship, you'll have these nice tools to swim well in an ocean overflowing with your own poop.

        • dionian a day ago

          As a Scala/FP newbie, i was stuck in this rut for years then i just stopped caring. it actually doesnt matter to me much. ZIO helped due to the eschewing of mathematical terms (compared to things like Cats ). In other words, you don't really need to know to get things done. Monads are incredibly intuitive once you start composing them.

        • eddlgtm a day ago

          Monads are just monoids in the category of endofunctors.

          /s

          Monads are, in my head, just a wrapper around a type. Or a box another type is inside. For example we can have an Int and we can put the Int in a box like Maybe<Int>.

          Imagine Python code that gets a value from a function that is an Int or None, then another function that takes an Int and returns an Int or None, then another function that takes an Int and returns an Int or None. How hellish is that to handle? If not None, if not none, if not none, ad nauseam...

          In Haskell I could use a Traverse function that takes a monad and passes the Int value to the next function or handles the None error, avoiding all the boilerplate.

          Other Monads are like the State monad - a box that contains some variables I want to maintain over functions.

          Or an IO monad to handle network calls / file calls.

          It's probably not a perfect analogy, but I work with functional languages and it tends to hold up for my beginner/intermediate level.

      • mrkeen a day ago

        > (Oh I forgot about monads.)

        This demands an equivalent article which takes the same form as TFA.

        A coworker complains about monads, so the manager insists all monads be taken out.

        So Brian gets to work on the codebase, removing all Lists, Optionals, Streams, Functions, Parsers, Transactions, ...

      • jen20 a day ago

        Also the unwillingness of so many enterprise developers to learn anything that wasn't commonplace in 2001, regardless of whether they were in the workforce by then.

    • jazzypants a day ago

      This is written by the same guy who made a big stink about how JS promises weren't pure monads and somehow that was a problem-- a zealot, in other words.

      • frozenlettuce 19 hours ago

        well, promises' computations start as soon as they are created, so they are not composable. and there' no cancellation/resource control as well. so I guess that the criticism is valid

  • flohofwoe a day ago

    When did method chaining become 'functional programming'?

    It's not 'functional programming' that makes the code unreadable, but overly long chains of array-processing functions. Sometimes a simple for-loop which puts all operations that need to happen on an array item into the loop-body is indeed much more readable.

    • Animats 21 hours ago

      You know, stuff like this, which I had to write today, for turning a 2D floating point height map into a 2D greyscale image.

              let max = self
                  .heights
                  .elements_row_major_iter()
                  .max_by(|a, b| a.total_cmp(b))
                  .unwrap();
              let min = self
                  .heights
                  .elements_row_major_iter()
                  .min_by(|a, b| a.total_cmp(b))
                  .unwrap();
              //  Scale into 0..255
              let range = (max - min).max(0.001);
              let height_array = self
                  .heights
                  .as_rows()
                  .into_iter()
                  .map(|r| {
                      r.into_iter()
                          .map(|v| ((((v - min) / range) / 256.0).round() as usize).clamp(0, 255) as u8)
                          .collect()
                  })
                  .collect();
      
      After Rust formats it, it's probably more lines than doing it with FOR statements. Every parenthesis matters. So much of the typing is implicit that it's not at all clear what's going on.
    • mrkeen a day ago

      > When did method chaining become 'functional programming'?

      As soon as you stop calling it "method chaining" and start calling it "function composition".

      If you chain together a bunch of methods ('.' operator) in an OO setting, that's called a "fluent interface". It's a sign of good design and is to be commended.

      If you compose a bunch of functions ('.' operator) in an FP setting, it's an unreadable mess, and you will receive requests to break it into separate assignment statements and create named variables for all the intermediate states.

    • jolux a day ago

      > When did method chaining become 'functional programming'?

      It's very similar to applicative style in FP. Conceptually, method chaining is equivalent to nested function application, it just comes with syntax sugar for specifying the `self` parameter.

      • Jaxan 10 hours ago

        A big difference is that it must include the thing the functions act upon (self). In functional programming you often leave that out. In FP you can also refactor little pieces of the chain into its own separate name (eg: smallest = sort >> head). This avoids long chains.

        • fuzztester 5 hours ago

          Interesting. In which functional programming languages (that you know), can this be done? That refactoring little pieces, I mean. Looks like it would help with code readability.

  • _aleph2c_ a day ago

    The manager solved the wrong problem. People should be sharing their tricks with each other. This was a perfect time to set up some peer-to-peer training.

    • jen20 a day ago

      Or in the age of AI, "have you asked Claude to explain what the code does so you can learn something?"

  • seanhunter a day ago

    By the looks of the code snippets he should have explained that he’s not doing functional programming, he’s just writing unnecessarily non-idiomatic python code. I’m sure his manager would have understood.

    • ndriscoll a day ago

      The code snippets are Scala. The first snippet is idiomatic. I can't imagine a team adopting Scala and complaining about functional programming though.

      • tengbretson a day ago

        I thought "idiomatic Scala" was an oxymoron.

        • cyberpunk 20 hours ago

          Does it still generate 17 page long stacktraces of nothing but $anon$fun? Good times.

      • mrkeen a day ago

        I've been there. One of the more functionally-minded devs wrote something to do with putting Keys and Values into some kind of json store, and a less functionally-minded dev complained about all the type variables - Ks and Vs I guess.

      • tiew9Vii 19 hours ago

        The article date appears to be around the time the company the author was working at got a new CTO who effectively said functional programming was banned. The Scala teams got broken up and some pro Scala staff managed out…

        Maybe it’s related, maybe it’s not.

  • notmyjob 6 hours ago

    Thought the title read, “How to _slop_ functional programming,” and got excited. “It’s happening!!!”

  • apalmer a day ago

    Ultimately the important thing is that the development team align on the style of programming that they will use at least per project. And the larger the codebase and more developers working on it the more important the consistency in implementing the style guide is.

    Imperative programming style has many advantages over functional for some problems. Functional programming style has many advantages over imperative for some problems.

    The only clearly 'wrong' approach is codebases where you can look at the code and determine a specific developer on the team wrote feature x because it fundamentally looks completely different from the other sections.

  • ChrisMarshallNY a day ago

    Well, this is one of the reasons that I like coding for myself. The pay sucks, but I don't have to bowdlerize my work.

    When someone pays me to write code for them, they get to call the shots; even if I think their judgement sucks.

    If they want to hire incompetent programmers, that can't understand even halfway-advanced code, then that's their prerogative, and I need to suck it up, and play by their rules; which may include the need for me to write code as if I just started yesterday, because it will need to be maintained by folks that, um, just started yesterday.

  • a day ago
    [deleted]
  • nor0x a day ago

    Would be interested in an article about stopping OOP. I see myself often drifting towards classes and abstractions once my code grows in complexity

    • OhMeadhbh 20 hours ago

      This is the video that's been going around. It's a bit long and could have been compacted into a 20 minute video, but if you have the time, it's a good overview for people without a computer science background.

      "Casey Muratori – The Big OOPs: Anatomy of a Thirty-five-year Mistake – BSC 2025" https://youtu.be/wo84LFzx5nI

      But if your point is there aren't any written articles about stuff like this, I agree. If they're out there, they're a bit outside the mainstream.

    • fainpul 21 hours ago

      You go into work and discover that a coworker isn't happy with some code you wrote because they don't like it. They go to your manager and tell them that you're being a problem by writing code they don't like. Your manager, being very skilled in conflict resolution, makes a technical decision to avoid whatever tool you used which caused the problem. In your case it was OOP.

      That's it. You've been told. No more OOP.

      The manager has figured out what's good for the business and you figure that listening is what's good for your job.

      • OhMeadhbh 20 hours ago

        Though honestly, having a manager that's interested in how software is made is a gift. My managers for the last 20 years have all been like "okay, let's do planning poker and everyone make sure that all the fields in your JIRA tickets are filled out."

    • fuzztester 17 hours ago

      google richard feldman (roc lang creator) talk about using just functions and data.

      don't remember exact title, sorry, but description i used above is close, iirc.

  • jondwillis a day ago

    This literally happened to me with Functional Reactive Programming, around the same time frame (2016.)

    I still have to advocate for minimizing mutable state. At least since then, popular languages and frameworks are putting in a ton of work to make functional programming look more like the imperative spaghetti that is still prevalent and taught.

  • yakshaving_jgt 21 hours ago

    I do love a bit of FP. Here's the canonical Hello, World! program.

        {-# LANGUAGE OverloadedStrings #-}
        {-# LANGUAGE QuasiQuotes       #-}
        {-# LANGUAGE TemplateHaskell   #-}
    
        module Main where
    
        import Foreign.C.Types
        import Language.C.Inline qualified as C
    
        C.include "<stdio.h>"
        C.include "<stdlib.h>"
    
        main :: IO ()
        main = [C.block| void {
          system("python -c 'print \"Hello, World!\"'");
          } |]
    • OhMeadhbh 20 hours ago

      Except that someone installed a different version of python on the target system that changes the semantics of the print keyword.

  • yohbho a day ago

    when I see the word function or someone uses parenthesis to call one, it clearly is fp. Management was clear that this would not be tolerated, this source is too addictet to the good pure shit.

    • OhMeadhbh 20 hours ago

      I wrote my own Lisp-like language that's a LLVM front end that explicitly uses greater-than / less-than angle brackets instead of parentheses for exactly this reason. When you walk by and don't look closely at it, it sort of looks like HTML so doesn't attract attention. And when a co-worker looked closer I just said it was a legacy templating engine and they really should just stick to python.

  • cies a day ago

    FP code is often easier to read (the article provides a case to that point).

    What gave FP a bad rep is, i guess, Haskell (and the "pure functional approach w/ monad transformer stacks").

    Every shop I worked at the devs were already at a level that they'd appreciate FP code: easier to read, refactor and test.

    The tendency is also towards FP: see the features in recent Java/C# version. Or languages that gain popularity recently (Kotlin, Rust) are more FP'ish than their predecessors (respectively Java and C++).

    • OhMeadhbh 20 hours ago

      The way I tell it is FP gives you more options for where to put the state and how to denote it. There are plenty of environments that let you mix OOP and FP and they're all a little bit funky, but not unworkable. I think the key thing here is the learning curve is a little steeper than OOP-ish languages. And since our universities are now really just trade schools, teaching kids which python libraries to use to feed data into a ML model someone else has built, there's little chance we'll see junior software developers who have been exposed to idomatic functional programming.

  • macintux a day ago

    (2016)

  • OhMeadhbh 20 hours ago

    My final days at Planet Labs involved me telling people "oh hey, I'm going to do this functional thing" and everyone saying "oh wow! that's great!" And then I went around to key engineers and explained in detail what I was doing in the code and they said "OMG! THAT'S AWESOME!" And then I checked it in and the response was "I HAVE NO IDEA WHAT THE FUCK YOU'RE DOING HERE!?"

    Yeah. I don't want to work with those people. Did I quit? Was I fired? I can't really remember. But ultimately, I moved on to a better environment.

  • imtringued a day ago

    Using Scala and then complaining about unnecessary complexity is a bit much. Like, if you made the technologically brave decision to use a niche language like Scala that isn't just a slightly better Java like Kotlin then you'd better expect to accommodate the inexperienced developers by training them, possibly via pair programming, instead of constricting the experienced developers.

    • hylaride a day ago

      Post was in 2016 when Scala was almost required to use certain big data tools (apache spark, etc), the alternative being python and its limitations.

      • dismalaf a day ago

        Not understanding very basic FP stuff like flatten and map (or flatmap) but using big data stuff is wild...

  • busterarm a day ago

    There's only one universal lesson that I've learned over my 25+ year career so far and it's that software engineering types are fragile creatures whose egos must be tended to. Delicately.

    This is becoming increasingly true as the years pass and the number of times I've had to drop whole mature architectures and reimplement something worse because some engineer's feefees got hurt can no longer be counted on my fingers & toes.

    • jtms 20 hours ago

      This is 180 degrees from teams I have been on some years ago - everyone took pride in being thick skinned and detaching their ego from PR reviews. Seems to be a generational thing

      • busterarm 14 hours ago

        I 1000% agree with you. I barely recognize this industry from where I started in it. Pretty sure once my mortgage is paid off I'm changing careers.

  • tsss a day ago

    If this code is too complicated for you then you should reconsider your career.

    • elevation a day ago

      TFA is indignantly reacting to the least charitable interpretation of what his employer has asked him to do. I'd like to know the honest shape of the code his manager rejected before judging the employer over this.

    • dismalaf a day ago

      Agreed. Especially if they chose to use Scala...

    • frhhfgf a day ago

      [dead]