Absolutely this. Discussions around OOP vs functional always seem to ignore this massive boon to productivity that OOP brings. Being able to create something of a DSL and map your business rules onto basic operations on this DSL is a huge win. The trick is to design your objects and operations such that the salient rules "rise to the top" of the stack, and thus can be easily programmed and verified at the highest level of abstraction.
Of course a DSL comes with its own drawbacks. Having to learn a new "language", with operations that are sometimes just renaming more basic operations has an extra cognitive load that can't be ignored. This is why there is a threshold of complexity below which you're better off just writing it straight imperative/functional style.
Hold on - how is this a concept specific to object-oriented programming? Creating a DSL for your problem domain and solving the problem in the new language is exactly what functional programmers have been doing for decades! E.g. here's a mini-DSL in Haskell for parsing CSVs, built on top of the Parsec parsing DSL:
cellContents = many (noneOf ",\n") -- match up to first comma/newline
remainingCells = (char ',' >> cells) -- comma => parse more cells
<|> (return []) -- else done
cells = do first <- cellContents
rest <- remainingCells
return (first : rest)
eol = char '\n' -- match newline character
line = do result <- cells
eol
return result
Building DSLs is most emphatically not something specific to OO programming.This simple logic of fitting smaller things together to make bigger things has always delivered superior results for me regardless of the language in use.
OOP seems ass backwards to me but everyone I talk to generally doesn't have a clue how FP is different to OOP!
Put another way: with Java or Python, you coerce your problem domain to fit the language. With Lisp and Haskell, you coerce the language to fit the problem. I personally like the latter approach, but I suspect it's also a matter of preference.
When you reach corporate scales, convention needs to aid in the grokking of the codebase.
I greatly prefer JavaScript in a functional style when I'm coding for myself, because I know the code in and out, but I prefer something more like C# when working on a team because then at least there is a set of more strict conventions we have to adhere to.
On the other hand, if you have a set of string programmers and a bunch of domain experts, the DSL style is not only faster but wait to maintain. Your core programmers should be good enough to deal with both the host language and the DSL with no difficulty, and your domain experts need only to understand the DSL, which could be easy since it's already optimized for their domains. I've certainly read about this approach being very successful with Haskell at a bank.
More generally, core logic should be easier to understand and maintain with a DSL. Perhaps this makes it harder to extend or repurpose the DSL without a good understanding of the host language and the codebase, but it makes working within the domain much easier, and makes it simpler to verify that the code is correct in regards to the specific domain.
Also, Javascript is not the best language for embedding DSLs; while it's not horrible, the syntax is inflexible and the semantics are somewhat limiting.
There's usually lots of complicated conditions; if the customer is grandfathered in, then apply rules R1, if they got discount X, apply R2X, otherwise apply R2. Then a new set of rules come in and you need to grandfather the old ones in, and so on; the conditions grow like weeds. When you have rule engines designed around decomposing these conditions, they make them easier to develop, test, they can do things like warn you when certain cases are impossible to reach, etc.