1. 16

I would just like to get opinions from experienced folks on what they would consider languages/programing paradigms that lead to readable/maintainable code AND code that is efficient. I would appreciate it if folks would also mention what domain they work in, since this answer will vary with domain (the tradeoffs will vary).

  1.  

  2. 17

    I don’t think you’ll get a definitive answer one way or another to be honest. We can all point to different ideas and say “that’s obviously lousy and never going to work” but paradigm goodness is a partial relationship. It’s much easier to point out flaws than to pick winners.

    My guiding rule for choosing a paradigm for a domain is always whatever supports the powerful and versatile forms of abstraction is the right choice. Even that this is somewhat misleading because everything comes in tradeoffs, some abstractions are pleasant to use in X and horrible and Y and vice versa.

    Anyone who tells you that their paradigm is the golden standard that all others should bow before is full of it… unless it’s FP of course (runs and hides)

    1. 7

      I’ve seen people write terrible unmaintainable heaps in every language. I don’t think its the languages, tools, or paradigms that are at fault here. Its that people often don’t care; don’t have the time to care; or aren’t aware of these paradigms in the first place.

      Sadly; not caring about writing readable and maintainable code seems to be the norm. Your company typically wants you to solve a problem, in a short amount of time. The goals are typically short-sighted and design rarely comes into play. Here is your burn down chart; now get cracking.

      1. 7

        I think functional programming (even in non-pure langs) leads to easy to read code. Thinking of a function as returning something rather than doing something makes it easy to reason about.

        I don’t think this has any relation to efficiency. You can still use local mutable state.

        Although in some cases using immutable state is actually faster, as can ben seen in Om, a ClojureScript wrapper for Facebook’s React.

        As for languages, I think Python is very readable. Maybe I could get used to Haskell, which is very maintainable, but… at some point I feel like keeping monad transformers in your head is not much different from keeping mutbale state in your head. I think Clojure strikes a good balance, although parens take getting used to as well.

        1. 7

          I feel like keeping monad transformers in your head is not much different from keeping mutbale state in your head

          Not even close. I know what ListT and an EitherT are at any given point in my program and they don’t add that much for me to think about. Partly due to parametricity.

          If I have untracked mutable state I have as many possible program paths as there are discrete values of my state combinatorially exploded by the number of sequence points in my program.

          They’re not even in the same order of magnitude.

        2. 6

          Quite a lot of my time is spent on a medium-sized front-end application written in TypeScript. I use strongly-typed functional programming techniques as guiding principles (expressive types, persistent data structures, applicative functors, monads), but given the nature of the application, this can sometimes lead to performance issues. So, when I need mutable state, for example, I use it, but I try to keep it as localized as possible. If I need global mutable state, I use the type system to represent it. This way, I have a small number of types of landmark in the code that make it easy to navigate when I need to (if I see type X, I know mutable state is involved, for example), and it is simple enough to teach these idioms to new developers when I need to.

          1. 6

            Python is readable. If you avoid over-engineering things with object orientation or functional programming styles, it’s also maintainable. Simple, decoupled functions with clear inputs/outputs and working unit tests will get you very, very far.

            As for efficiency, depends on what you mean. If you mean raw metal efficiency, Python gives you a couple of upgrade paths (e.g. Cython, pypy) and a couple of escape hatches (e.g. numpy). Python 3.4 also offers several concurrency models – thread-based, fork/join, and evented I/O based (asyncio).

            If you mean programmer efficiency, it has ipython as a REPL and ipython notebook as a wonderfully productive data exploration environment.

            Above all, keep it simple.

            I have been working with Python for over a decade. 5 years ago, I left a job where I was writing Java full-time (for 3 years) and built my own company atop Python technology (Parse.ly). We sell real-time analytics dashboards and content recommendation APIs for top websites. We need to handle scale, reliability, and efficiency (all three), and Python has never let us down.

            1. 2

              I completely agree that Python is very good for programmer efficiency: I went from C to Matlab to Python and I love writing code in Python. It’s good to hear that Python’s performance does not bother you. I make extensive use of numpy, no use of pypy yet - should try it out. Cython impressed me a lot, but it makes the organization difficult - now I have a bunch of .pyx files lying about and the ctypes declarations clutter up the source code and makes it look ugly to me.

              1. 2

                You should give Shedskin a try. It converts well typed Python into C++ with runtime performance on par with PyPy but lower memory consumption and much smaller startup times.

            2. 3

              In my experience, code reviews and/or peer programming are critical to writing readable and maintainable code. This is a question of good practices before being a question of languages and paradigms.

              1. 2

                Design by contract. Though language tooling is sparse. What is more efficient than NOT doing defensive programming? And not having to write a bazillion tests.

                1. 1

                  Can you qualify what you mean by “not having to write a bazillion tests”? Design by contract doesn’t replace software testing (Wikipedia Link). Do you mean that you can save testing of arguments whose values are outside the range defined by the contract?

                  1. 1

                    Sorry, I wasn’t specific enough. It doesn’t replace testing, but it does replace most unit testing.

                2. 2

                  I’m not going to talk about efficiency much. I’ve done that in the past (C++), but I don’t sweat it as much now.

                  I’m fond of approaches that emphasize low coupling, immutability, and isolation of effects. I’m prefer tools that take a hardass approach to these subjects because it’s been proven time and time again that devs simply ignore them unless the tools push them to do otherwise. In a perfect world, we’d use more discipline with more blunt tools; in reality, it seems everyone tries to skate by and “just use Rails,” (so to speak) and then somehow manage to be surprised when their velocity slows over time. I think FP’s separation of behavior from state is a better default than the current trend of OOP-based MVC frameworks that insist you make objects out of every domain entity you find.

                  1. 4

                    Software has affordances, just like door handles or teakettles.

                  2. 1

                    Hi Folks, I see that I’ve goofed a bit in the title (though not the description). I meant to write “AND efficient code” because my experience with Python suggests to me that often efficient code is more cumbersome looking (e.g. trying to avoid function calls) than the conceptually clean version and I wanted to figure out if there were languages that easily allowed both.

                    Thank you so much for this long list of answers! One popular answer seems to be functional programming because of the discipline it enforces on the coder. I don’t have any experience with pure functional programming, though I try hard to write Python in a functional spirit. One thing that I worry about is handling large data structures without mutability.

                    I often write functions that change a data structure in place e.g. filling out some fields based on the values of other fields. It is possible to split the data structure up into separate variables but it becomes conceptually cumbersome. It is also possible to copy the existing structure over to a new one while filling out the computed fields but that will be horribly time and space expensive.

                    What is the recourse in FP for such cases?

                    1. 3

                      It is also possible to copy the existing structure over to a new one while filling out the computed fields but that will be horribly time and space expensive.

                      What is the recourse in FP for such cases?

                      Some functional languages such as Clojure support “structural sharing” or “memory sharing”. This means that, for instance, if you “update” a hash-map by adding a new key and its value, then the new hash-map just points to the old hash-map as well as to the new key and value. Thus, no expensive copying is needed. And since the original structure is immutable, there will be no problems where the old hash-map is modified, changing the value of the new hash-map.

                      There are some examples of sharing in Stack Overflow – “Memory sharing of persistent data structures in clojure”. That answer describes that in the implementation details, a new vector made out of an old one only shares memory with the old one for vectors of length greater than 32 – otherwise, Clojure judges that copying the whole vector is more efficient.