3 ms·
It's not so much static typing as functional purity that guarantees design by contract. In a pure language like Haskell, you are forced to go out of your way an
by p4wnc6 10y ago
It's not so much static typing as functional purity that guarantees design by contract. In a pure language like Haskell, you are forced to go out of your way and do things with glaringly bad code smells to avoid designing by contract. In, say, Scala, F#, or ML, it's easier to subvert by e.g. liberally using mutation or references. In Clojure, it's even easier to avoid, since there's no static type checking, and, arguably in Python it's even easier still.
But even in Haskell or Idris, which require extreme lengths taken by the programmer to violate these things, you still can, and you could violate them in a small piece of code that gets wrapped and used all over, spreading the impurity.
So at the end of the day, no matter what language you use, these things always boil down to mere conventions that rely on trust of the system implementers.
You can trade away some conveniences in order to add extra hoops for convention-violators to jump through -- and you might interpret the existence of the extra hoops as "safety" or some kind of "guarantee".
This suggests there should still be use cases when you want conveniences of Python but also want to enforce some degree of hoop-jumping to deter convention-violators. Switching to another language instead, because now Python's lack of intrinsic hoop-jumping guarantees is a problem, comes with it's own costs, and if those costs are significant and related to the major wins that Python was bringing you, then it could easily instead be more effective to just create a few abstractions (and accept performance hits or whatever) to get the hoop-jumping you need in Python.
One very common instance where this arises in dynamic typing languages is with subclassing and inheritance hierarchies. You might have a metaclass Foo and you want isinstance(x, Foo) or issubclass(x, Foo) to evaluate True for a wide range of x, some not directly inheriting from x.
You can monkeypatch the __instancecheck__ and __subclasshook__ methods on the metaclass to override instance and subclass checking.
This is an example where you could get a lot of value out of it. By building that machinery right, later on you can check isinstance and then raise a TypeError, rather than try-excepting an attribute access or something, and you morph a try-except block that might have had a lot of complexity and been a target for code growth when new features get added, and turn it into effectively a much simpler assert statement.
You definitely wouldn't want to move away from Python just to get this kind of flexibility out of the box in some other language.
But as you ramp up the need for this kind of hoop jumping, say by building some kind of complicated static type checker that uses argument type annotations and fancy metaprogramming with the inspect and typing modules, now you might step back and consider whether, if all that really is needed, you wouldn't just prefer a statically typed language to begin with. And so on...
- paulddraper 10y agoSo to summarize, design-by-contact makes sense in mostly pure functional languages?
- p4wnc6 10y agoNo, "design-by-contract" is a matter of degrees, and pure functional languages tend to afford the most extreme degree. Most other languages have easier to access backdoor escape hatches, but also have other benefits, like faster dev time, wider standard library, less esoteric math knowledge needed to understand the language's core abstractions, etc. Depending on just how easy the escape hatches are, just how secure you'd like them to be, and just how valuable the other benefits are, you just make some engineering trade-off as to which sort of language makes the most sense. All I'm saying is that some part of this trade-off space includes staying in a language like Python for its benefits (access to a turn-key machine learning library and rapid development time, say) but needing to build your own machinery to eliminate some of the escape hatches (like decorator-based argument and return value constraints, as in the article).
- Chris2048 10y agomonkeypatching, instance checking and class-hook hacks aren't things I'd particularly value in Python, they certainly aren't deal-breakers wrt "major wins that Python was bringing you". I've rarely needed to use these things - I'd sooner make use of multiple-inheritance and use mix-ins. Compared to a language like Java, first the possibility of Mixins exists, as does Duck-typing. Further, types are not mandatory, or statically checked. Is is the fact that you don't need to do this is Python that is a win. The fact that (barring some extension) you can't is not an advantage. Optional checking, and dropping verbosity (no check by default, not having to declare something unchecked) is the best of worlds. > things always boil down to mere conventions that rely on trust of the system implementers This doesn't mean you should try to reduce dependance on this trust. The less checking you do, the more likely a n issue slips through the net. Furthermore, not all infractions are the same, we can forgive small things. Not everyone should be able to modify import, or class-loading mechanisms freely, or without supervision. > some kind of complicated static type checker that uses argument type annotations You can make this argument for, say, jumping to Julia. Does a complication in implementing in Python outweigh the complication of an entire new language? You could argue Python 3 is exactly this...