The biggest problem in software development is the slow down of value output over time caused by accumulating complexity. Anything that can help keep complexity down is a big win for long term value output.
The biggest problem in software development is the slow down of value output over time caused by accumulating complexity. Anything that can help keep complexity down is a big win for long term value output.
Functional programming, in my experience, created much more complicated code because it seems like the authors like to be a clever as possible.
But granted it needs to be part of a broader pressure to keep things simple, otherwise devs will like you say, find a way to overcomplicate.
One can mix functional and OOP code well by compartmentalizing the parts of the code that are in OOP style, and situating it in an overall functional context. For example, the methods of a class can certainly be written in a very functional style, and classes themselves can be instantiated within functions.
The mark of a skilled developer is to know which paradigm is the best to solve a particular problem, just like the choice of a data structure can greatly affect simplicity of code to address a problem.
What kinds of problems, if I may ask?
Some context. I'm sure I'm biased by a number of factors. One is that I started programming a long time ago in C and I was passing around structs with data and function pointers, so objects. Another one is that I used a number of different languages, currently Elixir, Python, Ruby, some JavaScript, often in the same week for different customers: I like easy to understand code because I this kind of projects with sparse teams is easier to maintain than layer over layer of heavily engineered one (so factories, metaprogramming, etc.) Third, I end up using a minimum common denominator of all those languages plus inescapable and/or great features: I wish all languages had Elixir's pattern matching. This approach works and it reduces the context switch between projects.
So, when I'm coding in Elixir sometimes I have to create large data structures and pass them around, piping them to Module1.fn1, Module2.fn2, etc. It's more or less the same than composing methods in OOP, just different syntax. Sometimes those modules become bags of functions too. What's more difficult to do in OOP is to engineer a class to accept arbitrary meaningful methods. In Ruby I often code methods that return a hash (Python dict, JS object) and create a pipeline of .map.map.map in their caller, like a |> pipeline in Elixir.
I prefer mixed paradigm approaches, where one can create the few classes needed to represent the few data types that are objects (Elixir's and Erlang's GenServer) and code everything else as functions. I have a couple of Django projects where the only classes are the mandatory database models and management commands. Everything else is functions in modules. That would be much more pleasant to code in Elixir (if it had classes instead of those noisy and uninformative handle_call, handle_cast, start_link.) Ruby has modules too but for some reason customers really like to create classes for everything, even two method ones like
class Command
def initialize(args)
...
end
def run
...
end
end
Command.new(args).run
which are quite funny to look at. That should be command(args)Encapsulation and extraction of complex functionality (yes, you should not do it for trivial one-liners).
In Ruby, classes get used a lot for this because, uh, why not, but you might as well just have a module method - it's just that the way modules are used in Ruby makes this slightly awkward because Ruby is after all biased towards "everything is an object".
But in, say, Haskell I might write instead:
module Command
run :: [String] -> IO ()
run = ...
-- some other file
import qualified Command
...
Command.run args
(not that "Command.run" looks like super idiomatic code in Haskell or anything like that, probably you'd structure your code differently, but it's the general principle)