8 ms·
I've come to the conclusion that the benefit dynamic typing brings to the table is to allow more technical debt. Now of course technical debt should be repaid a
by ema 7y ago
I've come to the conclusion that the benefit dynamic typing brings to the table is to allow more technical debt. Now of course technical debt should be repaid at an appropriate moment but that appropriate moment isn't always "as soon as possible". Let me illustrate, say you're adding a new feature and create lots of bugs in the process. Static typing will force you to fix some of these bugs before you can test out the feature. Then while testing out the feature you decide that it was a bad idea after all or that the feature should be implemented completely differently. So you scrap the implementation. In this case fixing those bugs was a waste of time. Dynamic typing allows you to postpone fixing those bugs after you're more certain that the feature and its implementation will stay.
- The_rationalist 7y agoAccording to this, languages statically typed but offering a dynamic type (any in typescript) allow best of both world. And using the right types is a matter of refactoring.
- roenxi 7y agoWhich is entirely plausible. If all the decisions about what data should be flowing where in a program have been made there is no particular reason not to have static typing. It won't make things worse, will enforce discipline and will probably catch bugs. As far as static types are feasible they are great to have. Clojure's spec is the gold standard I work to; if anyone has a better system it needs a lot more publicity.
- falcor84 7y ago>If all the decisions about what data should be flowing where in a program have been made there is no particular reason not to have static typing. But this is exactly the GP's argument, no? According to the agile philosophy (and my personal experience), we don't have enough information at the start of the project to make right decisions about all parts of the implementation - so we try to decide about just the most crucial architectural aspects and make the rest as easy to change at possible, learning as we go. From this perspective, the argument is that dynamic languages make it easier to postpone some decisions and allow us to quickly experiment with the things we've left flexible. And then, over time, once we're more confident in our decisions, we can put in the effort to repay this technical debt and lock things down to some desired level.
- jcelerier 7y ago> - so we try to decide about just the most crucial architectural aspects and make the rest as easy to change at possible, that is exactly why I like static typing - I can just change stuff and ask the compiler to kindly list all the places that need to be updated to accomodate for the change.
- zozbot234 7y agoThe hard part is not offering a "dynamic" type (which is generally just a variant type with a bunch of RuntimeType cases) but making the "static" and "dynamic" portions of the language truly, tightly interoperable. Generally, this implies that the compiler and runtime system should be able to correctly assign "blame" to some dynamic part of the program for a runtime type error that impacts "static" code, and this can be quite non-trivial in some cases.
- Vinnl 7y agoI agree, and I think that's one of the reasons TypeScript works so well, and why it's a shame that some people dismiss it beforehand because they generalise from other statically typed languages to TypeScript. Not only are you able to consciously take up some technical debt somewhere, but it's also clearly marked by the type system.
- david_draco 7y agoInteresting, I would have said static typing allows more technical debt. Illustrating example: Lets say you pass a double variable from some part of your code through 13 layers of APIs until it is actually looked at and acted upon. Now you realise that you not only need a double, but also a boolean. In dynamic typing, you can make a tuple containing both and only modifying the beginning and end points. In static typing you have to alter/refactor the type everywhere.
- toastal 7y agoAnd on layer 10 you missed that you were using and treating the argument as if it were the double. Now you have a bug that the type system would have solved. Or you can alias that input early if you know with a good certainty it has possiblity to change, and now you just update the alias and everything gets checked all the way down without a refractor.
- k__ 7y agoAFAIK, the problem here isn't "bad static typing" but "bad type inference" If you only have to write the type once at the top of the 10 layer, you don't have to do much to change it.
- codeflo 7y agoIt’s often considered bad practice to pass around raw values for that very reason. Introduce a (minimal) abstraction, that is, give the thing you’re passing through those layers a name. Then you can change the endpoints at will and still get static checking (as a plus, you can‘t accidentally pass the wrong bool, or mix up the tuple order). I agree with the GP‘s point that static typing forces you to do that kind of design work earlier. (Edit: You raise a good point, though. I think a lot of people run into this kind of problem with static typing.)
- caseymarquis 7y agoI think at that point you probably need a major refactor, regardless of the language being used. While I don't want to be in that situation, I'll typically use a helper class if I'm forced to pass something through 13 layers (a class containing all the arguments). However, that's pretty much the textbook example for designing with dependency injection in mind. Static typing won't fix a terrible architecture.
- pchiusano 7y agoIt’s true that with static typing, you are usually forced to propagate your changes to “your whole codebase” to get it compiling before you can run any of it. That stinks. However, it turns out you can lift this restriction in static languages, and this is what Unison does: https://www.unisonweb.org/docs/refactoring https://www.unisonweb.org/docs/refactoring In Unison you can implement a change and propagate it just far enough to run one little experiment. There’s no need to upgrade all your code at once.
- NicoJuicy 7y agoI actually rely on static typing to make architectural code changes. Why I would want to go to dynamic typing is beyond me. My performance/efficiency would greatly decrease. . The next change I will implement will likely be to not allow null in a property, which can be done in c# 8
- beders 7y agoThe idea is that you wouldn't need large refactorings in more dynamic languages in the first place since you are operating on a different abstraction level.
- iamaelephant 7y agoBest of luck with that.
- simplyinfinity 7y agoIn the JS based project i recently joined (previously i was writing C# ), what i notice is that in a team of 6 people 4 are doing refactorings every sprint for the past 8 months.
- yakshaving_jgt 7y agoNothing about static types nor dynamic types dictate what level of abstraction one must operate on.
- NicoJuicy 7y ago
- rightbyte 7y agoYou could to a temporary inherited class and later delete it and merge the change into the parent class, in a static OOP language.
- radicalbyte 7y agoIt's not so much allowing debt, it's that the barrier to entry is extremely low. You can hack around and get a feature "working" without having a great understanding of what you're actually doing. Statically typed languages will confront you with a lot of obscure errors and just generally get in the way. Eventually the good developers will eventually learn the language and get burnt by float32 / sql injection / xss / dates / not validating input and they'll learn enough to become extremely productive. Static languages have evolved a lot; now the standard error messages are far better and the tooling is amazing. At the same time a lot of dynamic features - usually reflection based - have come along and provided their own breed of arcane and nutty rules and errors which seem designed to catch out only the strongest developers (the ones which break SOLID are the best!).
- deleted 7y ago[deleted]
- thrwaway69 7y agoOne point based on my observation. Programmers coming from languages with good type system seems to have low probability of being a glueman. While good type system doesn't need to have static typing, in general they tend to have a way to explicitly declare things when you need to. The current dynamic language ground is riddled with linters and additional tooling that static languages have solved already. This costs developer time (god, the amount of time people spend on writing an eslint config and selling it.)
- chrisrhoden 7y agoWhat's a glueman?
- imhoguy 7y agoI read it as "duct-tape programmer", but I can be wrong here.
- dan00 7y agoEven this hasn’t to be only a feature of dynamically typed languages, like Haskells deferred typing shows. (https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#deferring-type-errors-to-runtime https://downloads.haskell.org/~ghc/latest/docs/html/users_gu...)
- danmaz74 7y agoTo reinforce your point: static typing doesn't just force you to fix some bugs earlier, but it also forces you to spend more time doing design upfront, often when you still don't have clear specs. That can be a big drag when you need to do a lot of experimentation/iteration.
- realharo 7y agoBut you don't need to have a final design straight from the beginning. You can start with only what you need right now and evolve the types/schema over time. In fact, proponents of static typing would argue that the types make your code easier to refactor later, because you will be aware of all the usages, and able to move things around with confidence. The drawback is that you need to make the entire codebase correct (or commented out) every time, not just some isolated piece you're experimenting on.
- chongli 7y agoIt isn’t even a benefit, really. Statically typed languages don’t force you to model things with types; you’re completely free to use strings and doubles everywhere and take on all the technical debt you want. People who are only used to dynamic languages may feel like a static language’s type checker is an overbearing teacher (from the 1950s) standing over your shoulder, just waiting to jump on you for every silly mistake! While that feeling is common and valid as you’re learning the language, when you become fluent you see the compiler as more of an ally. You can call on it with a keystroke and it will find many problems that would otherwise only happen at runtime, possibly months or even years in the future! Moreover, in more advanced languages (such as dependent typed languages), the compiler can actually infer the body of your functions which leads to a totally different style of programming that feels like a snippet plugin on steroids.
- arminiusreturns 7y agoWhat about JIT things like lisps? Maybe the benefits of dynamic typing are more geared towards things with a repl in particular?
- leshow 7y agoThere are static languages with REPLs, Haskell's GHC has a particularly good one.
- nearbuy 7y agoThe trade-off is that the bugs flagged by static type checking are usually quick to fix (eg: function expected an int but was passed a float). With dynamic typing, the subtle bug that arises from the function unexpectedly truncating your float could take an hour to track down and outweigh any time saved from dynamic typing. This applies even when trying out new experimental features.
- dboreham 7y agoYou're supposed to have written a test that catches that bug. Which of course also takes more time and effort than simply using a type checker in the compiler of a typed language..
- hhas01 7y agoDepends on the language. I do agree that lossy coercions are a Bad Idea; a [mis]feature added with the best of intentions and inadequate forethought (e.g. AppleScript, JavaScript, PHP). OTOH, a good [weak] untyped language should accept a float where an int is expected as long as it has no fractional part (otherwise it should throw a runtime type error, not implicitly round). Depending on how weak the language is, it could also accept a string value, as long as it can be reliably parsed into an int (this assumes a canonical representation, e.g. "1.0"; obviously localized representations demand explicit parsing, e.g. "12,000,000.00"). A good rule of thumb, IMO, is: “If a value looks right, it generally† is.” This is particularly true in end-user languages where conceptual overheads need kept to a bare minimum. (Non-programmers have enough trouble with the “value” vs “variable” distinction without piling it with type concepts too.) -- † Ideally this qualifier wouldn’t be needed, but there may be awkward corner cases where a simpler data type can be promoted to a more complex one without affecting its literal representation. That raises the problem of how to treat the latter when passed to an API that expects the former: throw a type error or break the “no lossy conversion” rule? There’s no easy answer to this. (Personally, I treat it as a type error, taking care in the error message to state the value’s expected vs actual type in addition to displaying the value itself.)
- marcosdumay 7y agoYet, dynamic types make it much more to refactor your software. So that technical debt costs way more to fix.
- hhas01 7y agoYep. Being able to work fast and dirty is a huge boon when testing ideas and figuring out the initial design, and it is deeply annoying that so many typed languages refuse to permit this. The faster you can write code, the faster you can throw it away again; and arriving at good code almost invariably requires† writing lots of bad code first. If writing bad code is made expensive (another example of Premature Optimization), authors may be reluctant to try different ideas or throw anything away. Ideally, you want a language that starts out completely untyped, efficient for that initial “sketching in code” phase. Then, as your program takes shape, it can start running type inference with warnings only, to flag obvious bug/coverage issues without being overly nitpicky. Then finally, as you refine your implementation and type-annotate your public APIs (which does double-duty in documenting them too), it can be switched to reporting type issues as errors. Unfortunately, a lot of typed languages behave like martinets, and absolutely refuse to permit any such flexibility, dragging the entire process of development down their level of pedantry and generally making early development an almighty ballache. Conversely, untyped languages provide sod all assistance when tightening up implementation correctness and sharpening performance during late-stage development; it’s pure pants-down programming right to the bitter end. Mind you, I think this says a lot more about programmers (particularly ones who make programming languages) than it does about languages themselves. -- † (Unless, like, you’re Donald Knuth.)
- brabel 7y agoIf you think it helps to quickly write some code without types (I don't really agree with that in general, maybe in some specific cases), but you want to, when you're happy with the design, just add the types, there are many languages that support that! On the top of my head: * Dart (just don't give a type and the variable is dynamic) * Groovy (add @CompileStatic or @TypeChecked when you're ready) * Racket (start using typed-racket)
- hhas01 7y agoYeah, it’s not a new idea; but I’d say “some” languages rather than “many”. Dylan’s another case; great pity it didn’t fare better. (I’d much rather be using it than Swift.) Also, none of them are in the mainstream, which creates general challenges to adoption. Fortune favors the entrenched. ... Another thing worth considering IMO is structural rather than nominal typing, which fits more with the “if a value looks right, it is” philosophy that untyped language aficionados enjoy without going for full-on pants-down duck-typing. ML favors this, IIRC (I really must learn it sometime). I find nominal typing can get a bit excessively OCD, demanding so much additional annotation as to obscure the code as much as clarify (“Hi, Swift stdlib!”). I also wouldn’t mind being able to define sets of permitted type mappings in advance, and letting the compiler figure out where in the code to insert (implicit) casts. For instance, if I’m passing a variable typed as integer where a float/string is expected, it really wouldn’t kill it to promote it automatically; most are perfectly happy to promote integer literals where a float is required after all. (And if I do want to know about it, which I usually don’t, I can always turn warnings/errors on.)
- pepper_sauce 7y agoStatic types enforced by a compiler catch more bugs in logic that can be encoded in the type system at build time. How costly are these bugs? It probably depends on context. For a business app/SaaS, is the compiler going to prevent your broken business rules from allowing -100 items to be added to a basket, crediting the "buyer"? I would say a developer who knows how to interpret requirements is more important here. On the other hand, a compiler is probably an amazing place for static types, but I don't write compilers and I'd wager most jobbing devs don't either. Predicate assertions instrumented to run during development catch an equivalent amount and more, since they can evaluate data at run-time. Dynamic types combined with argument destructuring allows for very loosely coupled modules. I can see it being similar to row polymorphism, but then you have to ask whether it's worth the extra time? In many business apps a significant portion of LoC is mapping JSON/XML to DTOs to Entities to SQL and back. If everything starts and ends with JSON coming from an unverified source, forcing it into a "safe space" statically typed business program is almost ignoring the forest for the trees, possibly even giving a false sense of security. It's (over) optimising one segment of the system; it's not necessarily a waste but it's probably time which can be better spent elsewhere.