Polymorphism definitely makes it much easier, and it is a core concept in OOP. I am not advocating for or against OOP, but “swapping behaviour at runtime” is one of the things it’s good at.
If you want to be able to swap out implementations, personally I’d much rather rust’s trait system (or Java or typescript’s interfaces) over what “OO languages” like C++ give you. I basically never want a strict tree of object types with inheritance.
This isn’t an OO idea. I’m pretty sure FP languages like Haskell or ocaml have something very similar.
I don’t know about Rust, but interfaces as in Java (or things like protocols in Objective-C) are fundamentally object-oriented features. It is at the core of the concept. Java is much more object-oriented than C++.
You can do something similar with overloaded functions and functions interface, at which point you’re re-implementing OOP without encapsulation and worse language support.
It’s certainly not a feature that is exclusive to OO languages. Haskell supports type classes which can do the same thing. Or ocaml’s module types. Rust traits. Etc. I don’t think anyone will accuse those languages of being object oriented.
Frankly C++’s abstract superclasses have, in my opinion, the worst syntax of them all to support the same feature.
I really don’t see this kind of polymorphism as a weakness of non-OO languages. If you wanna play “who wore it better”, Haskell and rust get my vote. Rust traits are strictly more powerful than Java interfaces since you can say things like impl MyTrait for T where T: Iterator<…> {}
It doesn't. Inheritance is about defining interfaces. It doesn't give you runtime swapability or anything like that; you need to wire that up yourself.
For example, here[0] I have a concise way to define two logger implementations as functions. You could also do it by defining those two implementations as classes. In either case, the implementation is essentially provided by defining a constructor. In FP world, the constructor is the class.
Swapping is done elsewhere through some layer of indirection. So in OOP maybe you'd have a singleton class that hands out and can update the current logger. In FP, you might pass a `() => Logger` or `IO Logger` to your application, which does the same thing. You could define your FP "singleton" as
Then you pass your getter to your main application, and your setter to whatever portion can do the setting (e.g. an HTTP handler). Your getter/setter might have more going on in practice (e.g. flushing and closing the old logger when swapping), but that's the basic skeleton. If you wanted, you could also put the two functions into a LoggerManager record type, and then it would be pretty much exactly the same as the OOP solution but without the incantations around classes and inheritance and "overriding" things.