4 ms·
The shapes example is pretty contrived so I don't really have an opinion on it either way. But imagine you have something like a File interface and you have imp
by vore 4y ago
The shapes example is pretty contrived so I don't really have an opinion on it either way. But imagine you have something like a File interface and you have implementations of it e.g. DiskFile, NetworkFile, etc., and you anticipate other implementors. Why would you do anything other than have a polymorphic interface?
- anonymoushn 4y agoI don't know, I've never done anything where code needed to have runtime dispatch on the kind of file it has without also knowing anything about what kind of file it has.
- vore 4y agoHave you never used C++ iostreams? Or the Python file abstraction? Or Rust std::io::Read/std::io::Write? Or Node.js streams? Or DOM Web Streams? Or Ruby files? Or Haskell conduits? Or hell, fopencookie/funopen in C? The abstraction is super common and allows you to connect streams to each other without worrying about the underlying mechanism, which 99% of the time I don't really think you want to worry about unless you're sure it's a performance overhead. And that's great, because I surely don't want to write specializations by hand for all the different combinations of streams I need to use if I don't have to.
- anonymoushn 4y agoI've used open(2) and the io_uring openat :) I use generic readers and writers every day, but they don't have any runtime dispatch. The question was "Why would you do anything except vtables and runtime dispatch?" One answer is that my code that uses generic readers and writers and gets monomorphized gets to also be generic over async-ness.
- saidinesh5 4y agoI think the shapes example is more of a dig at various game engines where you end up with a long trees of inheritance (physicsbody -> usercontrollable physics body -> renderable user controllable physics body etc..) as opposed to the recent trend of using something like an Entity Component System. Also I think he isn't specifically against "clean code" but how the first tool used by various "clean code advocates" seems to be polymorphism via inheritance. I have seen this enough in lots of Java codebases and "Enterprise C++ codebases". "We need X." "Oh first I will create an Abstract Base class for X, then create X, so we can reuse X nicely elsewhere when we need it". It is still on the developers to understand that they may not even need it but for them it is "clean code".
- adamrezich 4y agoI wish he would've gone a bit more into why people reach for polymorphism instead of a struct with a variant enum: because it's how OOP is taught, and how it easily maps to human understanding of most problem spaces. like you're making an RPG, so you make an Item class, and then you make Armor and Weapon subclasses, and then you make Helmet/Chestplate/Leggings/Shoes and Sword/Polearm/Axe/Mace/Staff subclasses, etc. etc., just because it fits your mental model of the problem, when in actuality, you could've totally avoided all of that complexity, and the ensuing boilerplate, just by sticking it all in one struct and calling it a day. this is not always the solution, but more often than not it is.
- zelos 4y agoThat does depend on how the abstraction is defined, of course. I once worked on optimising a 2D Canvas C++ class which had a nice top level virtual interface so you could replace it with a different implementation. It was also crazily slow because it defined: virtual void setPixel(int x, int y, int color) = 0; and then implemented flood fill etc in terms of that.
- int_19h 4y agoThis isn't necessarily bad if all the other operations are also virtual. The idea is that you can quickly get something working (e.g. when porting) just by implementing setPixel(), and then gradually fill in other primitives with properly hardware-optimized versions. And you do need a virtual setPixel() in any case because some API client might need that one call.
- Arelius 4y agoI'm sorry, why do you need a virtual setPixel? You can still easily get something working just by implementing setPixel without virtual dispatch, the linker has no problem inlining that call at compile time. If some arbitrary API needs it to be virtual it's easy to implement the virtual call in just that specific case, instead of burdening your entire system with a virtual call that'll always be static in practice.
- int_19h 4y agoFor the same reason why you need a virtual drawLine etc. Because some code wants to render something, and doesn't want to care if it's rendering onto an on-screen surface, into a file etc. Your entire system will not be burdened with virtual calls in places where you use concrete implementations (so long as they're final/sealed, anyway). The overhead is only there if you try to use the abstraction generically, but why would you do that in the case where the virtual call "will always be static in practice"?
- mgaunard 4y agoAnything that yields data in chunks will not really be affected by runtime polymorphism much. It's only a problem when it's done per datum.
- alfalfasprout 4y agoRight, but the point is you can avoid polymorphism within a given implementation but still keep it at a higher level. Heck, even codebases used in trading can use polymorphism for very high level interfaces (or async tasks) but hot code paths don't use it.