3 ms·
> When people refer to "typing", it is almost certainly reasonable to assume they are referring to static typing, as implemented by most languages. I don't thi
by kerkeslager 6y ago
> When people refer to "typing", it is almost certainly reasonable to assume they are referring to static typing, as implemented by most languages.
I don't think that's a reasonable assumption at all. Even if, as you assert, the average person doesn't understand that types exist in dynamically-typed languages, I don't think that means I have to conform to common misconceptions.
> Specs/contracts are cool but ultimately don't afford the same kind of descriptive and expressive power that a static type system does.
True, but not what I was talking about.
Could you explain what descriptive and expressive power is missing here?
class Square(Rectangle):
def __init__(self, side_length):
self.side_length = side_length
@property
def height(self):
return self.side_length
@property
def width(self):
return self.side_length
- james-mcelwain 6y ago> the average person doesn't understand that types exist in dynamically-typed languages Again, this is needlessly uncharitable as to what people mean when they talk about type systems, which is obviously about the capabilities of defining new types for program analysis, not that the runtime has an internal conception of types. > Could you explain what descriptive and expressive power is missing here? Sure! Looking at this, I have no idea what the size of side_length is, whether it's possible for it to be negative, or whether it's possible it to be null. Of course, unless you're writing purely square based software, most domains are more complex than this. But I still think it's pretty helpful to know the properties of the parameters being passed to build your square are!
- kerkeslager 6y ago> > the average person doesn't understand that types exist in dynamically-typed languages > Again, this is needlessly uncharitable as to what people mean when they talk about type systems, which is obviously about the capabilities of defining new types for program analysis, not that the runtime has an internal conception of types. There is nothing "internal" about the example I gave. That's valid Python code that creates a type. My definition (which happens to be the actual definition of the word) charitably assumes that people know dynamic type systems exist, so I don't think you can really accuse me of being uncharitable. If anything I'm being too charitable, as evidenced by this conversation. > Sure! Looking at this, I have no idea what the size of side_length is, whether it's possible for it to be negative, or whether it's possible it to be null. > Of course, unless you're writing purely square based software, most domains are more complex than this. But I still think it's pretty helpful to know the properties of the parameters being passed to build your square are! Do you really not know whether side_length can be negative or null? Or are you just saying that to be argumentative? If we're pretending we don't know obvious things, why not just go all the way and pretend we don't know that side_length is a number? As for not knowing the size: why would you want to have to know the size? The fact that this square will work with any numeric type is a feature, not a bug. Now, consider this (disclaimer: my C++ is rusty, and I didn't syntax check this): class Square: Rectangle { private: double sideLength; public: Square(double sideLength) { this.sideLength = sideLength; } } Let's evaluate this based on your complaints about the Python example: 1. The size of sideLength. Well, yes, this example does tell you what size it is. Which is rather annoying, since now you have to cast if you want to pass in an integer, and you might overflow your bounds. This is an annoyance, not a feature.[1] 2. We know that sideLength can't be negative because we know what squares are. The type doesn't enforce that. You could enforce that by using unsigned int, but then you can't handle decimals. And in either case, you can't use very very large numbers. I haven't worked in a static typed codebase which has an unsigned BigDecimal type, have you?[1] 3. We know that sideLength can't be null because we know what squares are. The type system technically also tells us that, but the type system telling us what we already know isn't particularly useful. [1] Haskell's numeric type can actually handle this much more cleanly than C++, as I mentioned upthread. But in typical Haskell, you'd probably just let the types be implicit in a lot of cases.
- uryga 6y ago> Could you explain what descriptive and expressive power is missing here? tldr: i haven't seen a runtime typechecker that handles generics and function types in a satisfactory manner. i've used various Python libraries for runtime type-checking (based on `typing` annotations) like `typeguard`. and they work okay for simple types, but suck for anything involving generics and function types. checking if something is a `List[int]` every time it's passed as a parameter is too expensive, because you have to go through the whole list (and you're out of luck if it's an Iterator[int] - can't traverse that without exhausting it). runtime typecheckers don't have enough information to check if something is a valid `CustomList[int]`. and they have no way of checking if e.g. a function (passed as a parameter) is actually a `str -> int`, at best you'll find out when you call it. and runtime checkers, at least the ones i've used, often end up requiring more annotations than i'd have to write in a type-inferred language. it might be possible to work around that to some degree, but I think that's a fundamental limitation – unlike a static checker, they only have info about code that already ran. so you'll have to annotate code like this: f xs = cons 'a' xs because a runtime checker can't "look into the future" and tell that based on the usage of `cons`, the only sensible type for `xs` is List[Char], so `f` must be of type `List[Char] -> List[Char]`.
- kerkeslager 6y ago> i've used various Python libraries for runtime type-checking (based on `typing` annotations) like `typeguard`. Let's just stop right there, since it's immediately clear you aren't answering the question I asked. You're talking about type checking, not descriptive and expressive power. The topic is whether dynamic types are suitable for domain modeling, not whether dynamic types provide static type checking. We all agree that dynamic types don't provide static type checking.
- uryga 6y agoalright, just to clarify, from the comment that introduced expressive power into the discussion: > Specs/contracts are cool but ultimately don't afford the same kind of descriptive and expressive power that a static type system does. i understand that as "the ability to describe/enforce the domain's rules". now, i guess i made a bit of leap, jumping to runtime typechecking. my thinking was that while modelling your domain, you might want to specify that e.g. `width` and `height` must at least be numeric (i know i would!); `typeguard` et al are a concise way of doing that in Python, but have their limitations. so static types can be more "expressive" if the tools i mentioned (generics and function types) are useful for describing your domain model, which i often find to be the case. looks like i missed the mark there; but in that case, i'm not sure what point you're making with that Square class. it's hard to say what's missing (or not) because there's not a lot there.