> This would be mitigated if programmers were very careful about declaring methods to be final if it's not perfectly safe to override them
Its always perfectly safe to override a method if you maintain its required features. Its rarely, if ever, perfectly safe to do so if you don't. The issue is correctly documenting the required features.
> The issue is correctly documenting the required features.
I'm not sure where the disconnect here is. First of all, "correct documentation" might exist in an ideal world, but it rarely exists in the real world. Given this imperfect world, it seems prudent to use technologies and idioms that mitigate the consequences of this imperfection to the best that we are able. Of course no solution is going to be able to eliminate such consequences completely, but that's no reason not to use solutions that offer some benefit.
Secondly, the OP claimed that OO design is generally superior to functional design because it's easier to support overriding of behavior and it's easier to override behavior. Neither of these claims is true. In both cases, attention to detail is required.
Furthermore, in the OO case programmers using a class often fall into the attractive nuisance pit of thinking that just because a method is there, they can and should override it, and programmers implementing a class often neglect to even consider what might happen if a subclass overrides some methods. It's not just a matter of documentation; it's a matter of not even considering the consequences of providing this flexibility by default.
Additionally, when you do parameterize behavior using OO idioms such as template methods, the fact that parameterization is occurring has been obscured, while doing so using the typical functional programming idioms represents paramaterization as, well paramaterization. Who could argue that representing something as what it is is not a good thing?
> Given this imperfect world, it seems prudent to use technologies and idioms that mitigate the consequences of this imperfection to the best that we are able.
Assuming that methods which actually mitigate those consequences exist, and all other things being equal, this is true. In the use cases for which class-based object-oriented design is well-suited, all other things are not equal between class-based object-oriented design to support providing base functionality with overriding in subclasses and using functions that take functions as optional parameters to provide base functionality with per-call overrides, even before considering whether, when used for that purpose, the functions-as-parameters approach actually mitigates anything.
Particularly, they are not equal in that the class-based approach avoids repetition and makes it clear the unit the behavior is attached to, while the functional approach does not. The functional approach is obviously cleaner and clearer for per-function-call parameterization, while the OO based approach is cleaner and clearer (unsurprisingly) for per-object or per-class parameterization.
> Secondly, the OP claimed that OO design is generally superior to functional design because it's easier to support overriding of behavior and it's easier to override behavior.
I haven't been defending OPs claim, I've been criticizing your response which argued that functions-as-parameters was not merely as good but actually categorically superior to OO design for this purpose.
> Neither of these claims is true. In both cases, attention to detail is required.
"Attention to detail is required in both cases" does not, if taken as true (which I have no problem with), refute the claim that these things are generally easier to support in OO.
> Additionally, when you do parameterize behavior using OO idioms such as template methods, the fact that parameterization is occurring has been obscured
No, its not. Inheritance is categorical rather than per-call parameterization, and so using it presents what is being done as exactly that. Using functional idioms for categorical parameterization either involves recreating class-oriented structures or conceals (and makes less DRY) the categorical nature of the parameterization behind per-call overrides.
> The functional approach is obviously cleaner and clearer for per-function-call parameterization, while the OO based approach is cleaner and clearer (unsurprisingly) for per-object or per-class parameterization.
Having programmed heavily in both functional and OO styles, I personally find the opposite to be true. I find the functional approach to be cleaner and clearer in general.
I particularly find template methods to be egregious because when you override a callback method, it's not immediately clear that what is being overridden is even a callback. Additionally, in programming languages that don't require an "override" declaration to override a method, it's not immediately clear that a method is being overridden, rather than just a new method being defined. And with multiple inheritance, this is even worse, because you might have to look in a zillion different other places to even determine this.
> Using functional idioms for categorical parameterization either involves recreating class-oriented structures or conceals (and makes less DRY) the categorical nature of the parameterization behind per-call overrides.
Recreating class-oriented structures? There's no work to do this, and there's nothing non-DRY about it. E.g., see the book JavaScript the good parts. It shows you how to define objects using either JavaScript's OO-based mechanism or using a Scheme-like functional approach. The functional approach is elegant and popular.
Its always perfectly safe to override a method if you maintain its required features. Its rarely, if ever, perfectly safe to do so if you don't. The issue is correctly documenting the required features.