> I would strongly prefer that the actual type system do this job instead. As a toy example, if a function is only meant to operate on "lengths" (i.e., non-negative scalar values) then that should be modeled in the types of its arguments, not in its name.
Have you never worked in a dynamically typed language?
> Folks who prefer dynamicly-typed languages are generally of the opinion that working with compiler error messages sucks.
This is not at all why I prefer dynamically typed languages. Try doing some JSON parsing in Haskell for a non-trivial payload (like something with nested objects) without wanting to pull your hair out.
> Do people who prefer dynamic languages actually use the benefits of dynamic typing in the first place? Changing variables from one type to another mid-stream.
I don't think people use dynamic languages for that ability. It's more for the ability to not have to explicitly think about types -- it's more natural that way.
> In the end, dynamic typing is just a way to make runtimes slow and building correct programs slower, in exchange for some entirely subjective aesthetic or ergonomic quality.
To be honest, I really don't understand what the benefit of dynamic types are supposed to be. People say it's easier and less hassle to get something up and running because you don't need type annotations but this isn't an issue with decent type inference and all you're doing is replacing compile time checks with runtime crashes.
> so an unsound type system (or no type system at all) is not the answer
And my unpopular opinion is precisely that. "Sound" typesystems enforced at the expense of the kind of cognitive load we see here will hurt rather than help the expression of real world problems in code.
Literally, casting a C function pointer through a void* is a better answer. I'm serious.
> If you use String for all your data types than you are no better than a dynamic language.
I once read a Haskell (I believe, may have been SML or OCaml, this was a while ago) tutorial (can't find it anymore) that did this. It was infuriating as it completely hid the benefit of the type system. Essentially, details fuzzy, it was creating a calculator program. Imagine parsing is already done and had something like this:
eval "add" a b = a + b
eval "sub" a b = a - b
...
Where the parsing should've at least turned those strings into something like an Operation type.
Sadly, I've seen similar programs in the wild at work (not using these languages, but with C++, Java, C#) where information is encoded in integers and strings that would be much better encoded in Enums, classes, or other meaningful typed forms.
> It makes no sense at all whatsoever that the front end logic should be more strict than the API!
It does to people who appreciate type information. To each his own. I can see the appeal of dynamic typing but I am pretty sure obvious errors (obvious if the compiler has type info) go uncaught.
> Heck, a function that's int x int -> int could be just about anything. How about string -> int?
That's because using types like "int" and "string" is not a real, valid use of a good type system. Instead, you'd typically have a function type of `newton -> sqMeter -> pascal`, or `username -> accessLevel`. All these types would be implemented through basic int and string types, but declaring them explicitly with very limited conversions inbetween would actually use type system to verify correctness of your code.
> > 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):
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.
Maybe I'm spoiled by other languages with more powerful type systems, but this is exactly what I want my types to do! Isn't this why we have type traits and concepts and whatnot in C++ now? If not for semantics, why have types at all, the compiler could figure out what amount of bytes it needs to store my data in, after all.
I use types for two things: to map semantics to hardware (if memory or performance optimization are important, which is rare) and to enforce correctness in my code. You're telling me that the latter is not a valid use of types and I say that's the single-biggest reason I use statically typed languages over dynamically typed languages, when I do so.
But even if that's not the case, why would I use a more general type than I need, when I know the constraints of my code? If I know that negative values are not semantically valid, why not use a type that doesn't allow those? What benefit would I get from not doing that? I mean, why do we have different sizes of integers when all the possible ones I could want can be represented as a machine-native size and I can enforce size constraints in software instead? We could also just use double's for all numbers, like some languages do.
> You can build a dynamic type system on top of a static one. The opposite is impossible. What else is there to even talk about?
We are talking about the value of different kinds of type systems and using them. Being able to build a dynamic one on top of a static one says very little about whether or not dynamic or static typing is better for actual usage. On top of this lots of languages have added gradual typing, so this idea that you cannot take a language that is not statically typed and add a type system seems misguided.
> A very obvious consequence of this observation is that there will always be far more boilerplate with dynamic typing than with static.
I hope you realize that this is not at all what reality looks like.
> instead of ever being able to know through the type system that you have a meaningful value.
That's... not what I'm looking for out of my type system. I'm mostly looking for autocomplete and possibly better perf because the compiler has size information. I really hate having to be a type astronaut when I work in scala.
So, I mean, valid point. And I do cede that point. But it's kind of like telling me that my car doesn't have a bowling alley.
> Every OOP language has different behaviour for different input types
Yeah. And I find OOP horrific.
> How would you feel about having to write `sqrt_int32(x)`, `sqrt_int64(x)`, `sqrt_float64(x)`,
What kind of savage wants the square root of an integer? I'd prefer if the language only had a single number type (maybe configurable at once by an external option) and a single sqrt function.
But if you talk about the general problem of naming functions differently according to their types, I feel that it's perfectly OK. A tiny price that I'm eager to pay for the large benefit of being able to identify which function is called just by looking at its name (and not at the---possibly yet undetermined---types of its arguments).
> Honestly I don't get why some people want to move to static types
I don't understand how anyone that has experience with dynamically typed languages and the insane runtime errors that can result from them would ever consider using a dynamically typed language. It's terrible and it actually provides little to no benefit in development speed. People always say development is faster in a dynamically typed language, this is not my experience. You need to actually run the program and step into it with a debugger in order to determine the type of anything at run time.
Given the popularity of typescript and how nearly all major internet companies have moved to typed versions of their dynamically typed languages it's clear to me the whole dynamic typing experiment has failed absolutely miserably.
Have you never worked in a dynamically typed language?
reply