5 ms·
What ends up happening in practice instead is that large projects using dynamic languages are hesitant to change anything like that. So if you didn't know the p
by spion 10y ago
What ends up happening in practice instead is that large projects using dynamic languages are hesitant to change anything like that. So if you didn't know the problem domain sufficiently to guess all the interfaces up front correctly, you end up in a pretty bad place.
In contrast, making such changes when you have types is an order of magnitude easier.
The equivalent would be to have integration tests that exercise the entire system with all its functions and code paths checking for basic data compatibility. Seems like a worthwhile thing to have to me, as its not easy to achieve at all.
Not to mention you have a tool to answer "if I change X, what will be affected" - even if you are not changing the type. Just pretend that you changed the type of X (e.g. change "name" to "firstName"), and see all the places that don't compile now. So much better than grep!
- jstimpfle 10y agoMost architects of large software projects agree, in my perception. As a counterpoint, I figure that something is wrong if changes routinely affect large parts of the code. The idea is that data is "in the interfaces" instead of global. Except for a few "god objects" changes should be pretty local.
- spion 10y agoYes, what is wrong is our understanding of the problem domain. Or even the stakeholder's real understanding of the problem domain :) Maybe we should stop working on types and focus on easy, high quality languages and tools which will enable other professionals to easily solve problems from their domain doing all the programming themselves. And not just languages - heck, it takes 2 hours just to set up a basic React Native stack for a "Hello World" app.
- crdoconnor 10y ago>What ends up happening in practice instead is that large projects using dynamic languages are hesitant to change anything like that. I have seen this "code fear" in both statically and dynamically typed languages and fixed it. When I have worked through it, it has been roughly an equal amount of work to solve it in dynamically and statically typed languages. Integration tests are usually the key to getting over the code fear hump. >The equivalent would be to have integration tests that exercise the entire system with all its functions and code paths checking for basic data compatibility. Integration tests that exercise every relevant user story are a prerequisite for avoiding code fear in any language - statically OR dynamically typed. In addition to this sprinkling additional type checks around the code helps give confidence that the code is indeed working and gives you additional freedom to refactor. This is something I do when: A) I encounter a type related error that an assertion would have caught. B) I was previously confused about what type a var is or if just by looking at it I think somebody else might be confused. People who have terrible test suites for their code written in statically typed languages are usually insistent that it's best practice to have types checked by the compiler. This means two things about their development practice A) they don't drive development with tests (TDD) and B) they often push unexercised code.
- spion 10y agoThis is a claim I hear often. I don't think its true. Its possible to have integration tests for every user story, yet still not cover even a fraction of the possible code paths due to an explosion of different possible outcomes and inputs. The great thing about types is that they're guaranteed to cover all code paths. They can't guarantee certain categories of things, but for the things they can guarantee, they easily provide 100% coverage, something that is impossible with integration tests alone. They are also a tool to greatly reduce the number of allowed inputs and outputs for most code, which in turn greatly reduces the number of tests that actually need to be written. Runtime type assertions only provide one level of that: if you are sending nonsensical input to a method in an uncommon code path not exercised by your integration tests, the runtime type check will probably fail too late (in production). While runtime assertion can tell you if you call your code wrong at runtime, they can't answer "What are all the places that send incorrect input to this method?" Types can do that. Not to mention that runtime assertions can be costly, unnecessary and very repetitive if added for every method. Comparatively types are free in terms of both performance and programmer effort, especially in languges with good inference. In short, tests cannot replace types, just like types cannot replace tests. In the areas where they do overlap, types are a way better, more efficient, more tooling friendly choice.
- crdoconnor 10y ago>Its possible to have integration tests for every user story, yet still not cover even a fraction of the possible code paths due to an explosion of different possible outcomes and inputs. Which is true in any language. Having a hierarchy of integration tests helps to offset the combinatorial explosion, as does sanity checks that eliminate invalid code paths. These perform a very similar function to static types but you don't have to use them in prototype code and you can add them retrospectively in production code. >The great thing about types is that they're guaranteed to cover all code paths. Types are something every programming language has. >They can't guarantee certain categories of things, but for the things they can guarantee, they easily provide 100% coverage, something that is impossible with integration tests alone. Inserting type and sanity checks can achieve virtually the same guarantees as static typing when you have a comprehensive test suite. When you don't have a comprehensive test suite, static typing may seem more attractive but it's a false sense of security. >if you are sending nonsensical input to a method in an uncommon code path not exercised by your integration tests, the runtime type check will probably fail too late (in production). Given that you have untested code, you have a higher likelihood of bugs. Period. In statically typed languages too. In dynamic languages those bugs are likely to manifest a bit differently. Yay? >While runtime assertion can tell you if you call your code wrong at runtime, they can't answer "What are all the places that send incorrect input to this method?" Types can do that. Arguments that argue best practice that start with with "given that I have poor test coverage..." are not especially convincing. >Not to mention that runtime assertions can be costly Runtime assertions are costly in CPU time. Static typing is costly in programmer time. CPUs are cheap, programmers are not. >unnecessary and very repetitive if added for every method. Oh yes, which is why I don't add them to every method - just methods where I am convinced they will be useful. >Comparatively types are free in terms of both performance and programmer effort Static typing never comes for free and it is often highly inappropriate (which is why many statically typed languages have dynamic typing bolted - e.g. see reflection in Java). >In short, tests cannot replace types, just like types cannot replace tests. You were arguing above that static typing was great because it caught bugs in untested code. That certainly sounds like you think it is replacing tests.