3 ms·
Not just Python. Most languages with constructors behave badly if setup fails: C++ (especially), Java, JavaScript. Complicated constructors are a nuisance and a
by echelon 2y ago
Not just Python. Most languages with constructors behave badly if setup fails: C++ (especially), Java, JavaScript. Complicated constructors are a nuisance and a danger.
Rust is the language I'm familiar with that does this exceptionally well (although I'm sure there are others). It's strictly because there are no constructors. Constructors are not special language constructs, and any method can function in that way. So you pay attention to the function signature just like everywhere else: return Result<Self, T> explicitly, heed async/await, etc. A constructor is no different than a static helper method in typical other languages.
new Thing() with fragility is vastly inferior to new_thing() or Thing::static_method_constructor() without the submarine defects.
Enforced tree-style inheritance is also weird after experiencing a traits-based OO system where types don't have to fit onto a tree. You're free to pick behaviors at will. Multi-inheritance was a hack that wanted desperately to deliver what traits do, but it just made things more complicated and tightly coupled. I think that's what people hate most about "OOP", not the fact that data and methods are coupled. It's the weird shoehorning onto this inexplicable hierarchy requirement.
I hope more languages in the future abandon constructors and strict tree-style and/or multi-inheritance. It's something existing languages could bolt on as well. Loose coupling with the same behavior as ordinary functions is so much easier to reason about. These things are so dated now and are best left in the 60's and 70's from whence they came.
- fouronnes3 2y agoHow would you do traits in Python?
- echelon 2y agoI'd really need to think long and hard about it, but my initial feeling is that we'd attach them to data classes or a similar new construct. I don't think you'd want to reason about the blast radius with ordinary classes. Granted, that's more language complexity, creates two unequal systems, and makes much more to reason about. There's a lot to juggle. As much fun as putting a PEP together might be, I don't think I have the bandwidth to do so. I would really like to see traits in Python, though.
- svilen_dobrev 2y agothere were "traits" in python :/ . Search for pyprotocols. (searching myself.. not much left of it) 2004 - https://simonwillison.net/2004/Mar/23/pyprotocols/ https://simonwillison.net/2004/Mar/23/pyprotocols/ some related rejected PEP .. https://peps.python.org/pep-0246/ https://peps.python.org/pep-0246/ talking about something new to be expected.. in 2001?2005? no idea did it happen and which one would that be..
- dragonwriter 2y agoPossibly Guido, in that rejection, was talking about what ended up as ABCs, original PEP: https://peps.python.org/pep-3119/ https://peps.python.org/pep-3119/ Further work was done in this area, building on ABCs, with Protocols, original PEP: https://peps.python.org/pep-0544/ https://peps.python.org/pep-0544/
- vaylian 2y agoPython has a trait-like system. It's called "abstract base classes": https://docs.python.org/3/library/abc.html https://docs.python.org/3/library/abc.html The abstract base class should use the @abstractmethod decorator for methods that the implementing class needs to implement itself. Obviously, you can also use abstract base classes in other ways, but they can be used as a way to define traits/interfaces for classes.
- dragonwriter 2y agoProtocols, too (they are defined on top of ABCs) https://typing.python.org/en/latest/spec/protocol.html#protocols https://typing.python.org/en/latest/spec/protocol.html#proto...
- gpderetta 2y agoProtocols are definitely the closest thing to traits in Python. ABC are pretty much OoO interfaces.
- rcxdude 2y agoThe annoying thing is you can actually just use the rust solution in C++ as well (at least for initial construction), but basically no-one does.
- whatevaa 2y agoIn C#, it is also not a good idea to have constructors with failable io in them.
- neonsunset 2y agoYup, and IO being async usually creates impedance mismatch in constructors. Had to refactor quite a few anti-pattern constructors like this into `async Task<Thing> Create(...)` back in the day. No idea what was the thought process of the original authors, if there was any...
- hdjrudni 2y agoI don't have enough experience with traits, but they also sound like a recipe for creating a mess. I find anything more than like 1 level of inheritance starts to create trouble. But perhaps that's the magic of traits? Instead of creating deep stacks, you mix-and-match all your traits on your leaf class?
- echelon 2y ago> I find anything more than like 1 level of inheritance starts to create trouble. That's the beauty of traits (or "type classes"). They're behaviors and they don't require thinking in terms of inheritance. Think interfaces instead. If you want your structure or object to print debug information when logged to the console, you custom implement or auto-derive a "Debug" trait. If you want your structure or object to implement copies, you write your own or auto-derive a "Clone" trait. You can control whether they're shallow or deep if you want. If you want your structure or object to be convertible to JSON or TOML or some other format, you implement or auto-derive "Serialize". Or to get the opposite behavior of hydrating from strings, the "Deserialize" trait. If you're building a custom GUI application, and you want your widget to be a button, you implement or auto-derive something perhaps called "Button". You don't have to shoehorn this into some kind of "GObject > Widget > Button" kind of craziness. You can take just what you need and keep everything flat. Here's a graphical argument I've laid out: https://imgur.com/a/bmdUV3H https://imgur.com/a/bmdUV3H