Please note (if you can't be bothered to read the actual post before responding), I am _not_ saying that static typing has no value. I'm saying it has both a value _and_ a cost, and too many arguments seem to consider only one of these.
Please note (if you can't be bothered to read the actual post before responding), I am _not_ saying that static typing has no value. I'm saying it has both a value _and_ a cost, and too many arguments seem to consider only one of these.
These are not the same thing. See Haskell for a counterexample.
EDIT: I see that Haskell is mentioned in the comments. Still, I don't see any understanding there that static vs. dynamic is not the issue.
note: Yes I know the compiler can't actually infer all types all the time, but the exceptions are obvious and require type information in dynamically typed languages too (ie. convert a string to an int: you have to tell it you want an int, you can't just say "convert this string" and have it guess what you want it converted to).
Declare types of all named functions (i.e. not lambdas). This serves two purposes.
First, it makes your code more self-documenting. When I look at a function, probably the first thing I want to know is: "What are its parameters?" Type declarations on functions give you a trustworthy and highly readable answer to that question.
Second, it makes type errors easier to understand. If you don't explicitly declare types, the compiler will perform type inference. When your code has a type error, the compiler can infer a nonsensical type for a function, yielding a hard-to-understand error message. If your function has a type declaration, the error message is more likely to point you to where your actual mistake is.
Rarely declare types on anything that's not a top-level function definition. For example, if I want to call (foo (bar baz)), I don't need to add separate type declarations to baz, (bar baz), and (foo (bar baz)), even though each quite possibly has a different type. On the other hand, if you want to remind yourself of the type of some inner expression, or you think it'll make the type errors more readable, you can add the declaration. It won't hurt you.
While type inference is cool, it's only really useful when I'm playing inside GHCi. Also - it can often improve the quality of a program a lot by thinking about your types first, declaring them, then filling out the function definitions (that is often how I do it).
From TFA:
"They write test cases for every identifier ever used in the program. In a world where misspellings are silently ignored, this is necessary. This is a cost."
So in a dynamic language the cost is still there. In fact, it's probably higher than in a static language because you have to build at least some of the infrastructure yourself. Well, I imagine some people would argue that in a dynamic language the cost is optional. And again, I point to type inference where sophisticated static languages achieve the same thing.
How does "dynamic typing"="misspellings are silently ignored"? Maybe in some languages but that's not a corollary of dynamic typing.
Disclaimer: Haven't read TFA so shoot me down if the context makes this clear
And yes, you'll need to do a little bit of infrastructure on the test cases, but you'll gain the extra power that comes with dynamic types, that makes everything (even writting tests) much easier. And, of course, your codebase can be much less "huge" with that extra power.
Anyway, all that thing is a big red herring. Nobody decides to create a huge codebase.
What "extra power" are we talking about here? If we're talking about the power to pretend a bool is a string or vice versa, then no thanks, I'll pass. I suspect that a number of the things you are referring to that give you "extra power" are actually things that don't have anything to do with the type system like first class functions, tuples, etc. Static languages can have those things too, you know.
That's weak typing. JavaScript does this, Python and Ruby don't.
Typically when people bring up the power of dynamic languages it's in the ability to alter definitions. The ability to extend existing classes/objects is pretty powerful, and isn't always trivially duplicated in static languages.
Many concepts that can be painful in statically typed languages (creating/programming against new interfaces to existing types, reflection, delegation, etc) are easy as breathing with dynamic types. It's not a matter of can vs. can't so much as easy vs hard.
s/pretend a bool is a string or vice versa/not know whether an object even has a certain method because it isn't added until runtime/
I'll grant you that this type of thing is more difficult in static languages. But in my experience, the need for this kind of dynamic behavior is quite rare in terms of lines of code. So I'll trade it away any day of the week for the ability to reason about my code with more guarantees. At their core, all bugs are situations where the programmer's expectations didn't match reality. In my experience programmers who are good at debugging are good at being able to question as much of what could be going wrong as possible. Strong static types are about reducing the amount of things we have to question.
Agda is offended.
You can hack code together and forgo documenting it, but it's a form of technical debt. When creating a minimum viable product for a startup, taking on some forms of technical debt probably makes sense, but by the time you're talking about huge code bases, you're probably past the startup phase (I hope) and you're already up to your neck in technical debt.
It should be easy to integrate into any editor.
Anyway, my point is not that it's difficult or bad: it's that it's has _some_ cost.
But then the compiler has the capacity to write/emit the type declarations.
Not the case in Haskell, at least. Most Haskell code out uses type signatures.
> Conversely, a requirement that for explicit type declarations makes the use of generics more cumbersome.
True enough, but you can't have your cake and eat it. Either you want explicit documentation-by-types, which has a cost, or you don't (which also has a cost in terms of maintenance).
For me, I've looked inside one of the biggest Python codebases in existence (the EVE Online client). It's a freaking mess. A simulation of static typing but mostly they just passing freaking tuples around. Everything is a tuple in a tuple in a dictionary in a tuple in a homegrown table.
There are a variety of languages that don't impose explicit typing on you to enable static typing. OCaml and Haskell are popular options. Scala makes the compromise choice of having explicit function signatures with inferred typing elsewhere.
If the only thing you're using the type system for is to make sure arguments line up, you're barely using it at all. When your types can encode (and thus, ensure) aspects of your program logic, that is when static typing becomes useful.
For C++, note the now accepted use of `auto` and `decltype` which together remedy a number of issues from C++03 and prior.
You mean "in which I pose the rather obvious strawman". What scaffolding? What is this mysterious "extra stuff" people give vague names like "scaffolding" to that I've never encountered despite programming in a statically typed language all the time?
And while it's valuable to talk about popular languages in terms of the kind of static typing people are likely to encounter... they also have really outdated static type systems. While you can argue that it's likely that you'll bump into Java/C/C++ when working with static types, it's pretty invalid to argue generally about static type systems using them as examples. Things have just come a really long way.
No, because the affordances and limitations of static typing are not the same as the affordances and limitations of static typing as implemented in Java/C++. You can't infer very much about static typing from statements made about those languages, and you can't infer much about those languages from statements made about static typing, so it follows that you can't freely substitute one for the other in these discussions.
(not necessarily directed at you personally)
If the function has to handle different types in different ways, you'd have to write it again anyway. Otherwise, you could have used templates.