5 ms·
> Is it really different from yours? Of course it is. In your example you are explicitly breaking factorial. In my example I'm just defining `pred`, and it's
by ernst_klim 7y ago
> Is it really different from yours?
Of course it is.
In your example you are explicitly breaking factorial. In my example I'm just defining `pred`, and it's unexpectedly and implicitly breaking factorial.
Behavior of the factorial depends on explicit argument, behavior of the factorial method depends on implicit recursive call.
It's the same as having a global variable and a locally passed argument.
let x = ref 0;
let f () = x + 1;
x := 42;
let = f ();
is not different from
let f x = x + 1;
let y = f 42;
in a trivial example, but in a complex example with a complex state, the implicitness of the former would cause bugs.
Same with open recursion, your code simply mocks a trivial example. In a complex example where methods rely on state and locks, implicit redefinition of a mutually recursive methods causes bugs hard to debug and comprehend, since you can't say what exact virtual method is called (you shouldn't even know that, that's the point of OOP).
- protomolecule 7y ago"In your example you are explicitly breaking factorial. In my example I'm just defining `pred`, and it's unexpectedly and implicitly breaking factorial." In all mainstream languages with OOP features methods by default are not virtual and cannot be overridden by a successor class. You cannot accidentally create an opportunity for breaking factorial by defining 'pred', you'd need to explicitly declare 'pred' as virtual. If you don't do it, factorial cannot be broken and there is no problem. If we pretend that you did it, the question is how is it different from taking 'pred' as an argument in the functional example?
- ernst_klim 7y ago> by default are not virtual and cannot be overridden by a successor class. Non virtual methods has nothing to do with OOP, they are a mere sugar for function calling. We are talking about OOP, which is defined by late binding/open recursion, i.e. virtual methods, and the perils of open recursion. > You cannot accidentally create an opportunity for breaking factorial by defining 'pred', you'd need to explicitly declare 'pred' as virtual. If you don't do it, factorial cannot be broken and there is no problem. Ok, it seems you are confused by the trivial nature of the example, here is a real world example: https://gitlab.freedesktop.org/gstreamer/gstreamer-vaapi/merge_requests/95/commits https://gitlab.freedesktop.org/gstreamer/gstreamer-vaapi/mer... A decoder implementation calls GstElement's `change_state` method, which calls (through the long chain of calls) child's decide_allocation which calls parent and so on and so forth and in the end you have a deadlock. This is pure OOP, where each Class provides needed virtual methods for state change, buffer preparation, allocation etc, and they calling each other in an obscure recursive manner. This leads to fancy implicit bugs if you calling methods without understanding how they work and where they are implemented, which is impossible, since methods could be reimplemented later. > If we pretend that you did it, the question is how is it different from taking 'pred' as an argument in the functional example? How do I know that `decide_allocation` method is calling a method I'm going to reimplement? How do I know that decide_allocation would be reimplemented in some future decoder? In your example you explicitly redefining factorial. In my example I'm just redefining pred without caring about factorial and not even knowing that factorial calls pred (abstraction, remember?).
- protomolecule 7y ago"Non virtual methods has nothing to do with OOP, they are a mere sugar for function calling." By this logic virtual functions are mere sugar for tables of functions and as such have nothing to do with anything besides functions and tables. "We are talking about OOP, which is defined by late binding/open recursion" Oh, well, definition of OOP is a moving target, everyone defines it as they like, but when we are talking about trillion dollar mistakes, OOP certainly implies C++, Java, C# and such simply because Smalltalk and CLOS have been used on the far smaller scale. Having established this, I'm sure you would agree that saying that OOP prohibits or discourages non-virtual functions is unreasonable. It does introduce virtual functions, but like every tool, they shouldn't be abused. "a real world example" Looks like a poor approach to concurrency or an example of abuse of virtual functions, or both. "How do I know" Well, a method you are going to re-implement needs to have a clearly defined contract. If it doesn't, it is either not supposed to be reimplemented or the codebase is of poor quality. You can look at the set of virtual functions of a class as an interface. In the case of a plain interface you wouldn't ask 'how do I know how this interface is used?' - you don't and you can't, you just implement the contract. The same with re-implementation of virtual functions. "In your example you explicitly redefining factorial" No, I'm defining a new enhanced math function that reuses some existing code without understanding of its contract. Just like you do in your example :)
- ernst_klim 7y ago> Oh, well, definition of OOP is a moving target No, open recursion is the only thing that makes OOP a distinct paradigm. Without `self` you don't have OOP. [1] > No, I'm defining a new enhanced math function Yeah, no, you are redefining factorial in terms of different `pred` functions, and I'm simply defining `prev` without redefining factorial. [1] https://journal.stuffwithstuff.com/2013/08/26/what-is-open-recursion/ https://journal.stuffwithstuff.com/2013/08/26/what-is-open-r...
- protomolecule 7y ago"No, open recursion is the only thing that makes OOP a distinct paradigm. Without `self` you don't have OOP." I'm not arguing that open recursion is part of OOP definition, I just disagree that C++ and C# aren't OOP since they have it as an opt-in feature. You yourself happened to mention encapsulation as part of OOP in your original comment. While you can call bundling data and functions together a sugar, limiting the scope of data to class' methods is essential. Consider implementation of a hash map. Isn't it nice that the state is accessible only to {contains, get, set}? After auditing these three functions you can be sure that the invariant is never violated. How would you ensure that in FP? Thanks for the link, I myself happened to explain the complexity of virtual function contracts in the same way, nice to have it written down in case I need it again.
- timw4mail 7y agoWhat? Every language I've used you can easily override a parent method with a method in the child class.
- deleted 7y ago[deleted]
- gpderetta 7y agoNot in C++ unless a method is explicitly marked as virtual. IIRC C# behaves in the same way. In Java, D, all methods are virtual by default. Most dynamic languages also behave in the same way (due to pervasive late binding).
- protomolecule 7y agoI haven't thought of Java. As the sibling comment says, it's the case for C++ and C#. In case of Java I agree with ernst_klim's critique, to me it's a poor language design choice. Dynamic languages are a mess anyway, but that's just my opinion :)
- pjmlp 7y agoJava went with Smalltalk/Objective-C/Modula-3 defaults, because it was heavily inspired by them in language sematics. However you can still terminate a virtual chain, or not offer it at all with the final modifier.
- dragonwriter 7y ago> In all mainstream languages with OOP features methods by default are not virtual and cannot be overridden by a successor class. Ruby, Python, and Java (among others) are relatively mainstream, but allow this (the former two have ways to do this with individual instances as well as child classes.)
- protomolecule 7y agoYes, that sentence was wrong and if you say that ability to override any method is the essence of the true OOP I would agree that the true OOP is a disaster.