> I find dynamic typing similar to how unit testing is a forcing function for writing composable code, it acts as a forcing function to writing simple code; simple to read and simple to comprehend.
I don't agree with this argument, I think it's akin to saying "I like driving blindfold because it makes me drive slower". You can use linters with limits on line length and number of params if this is a goal for you, no need to go indirect with the restrictions.
I agree that typing is not the only solution, I'm just saying that I think the ROI is massive in types, so I think this should be the first tool people go to. Almost no investment, and a lot of benefit.
I agree with "code is the documentation", I even made a point about documentation in the post, but I argue that typing is part of the code. So "the code is the documentation, and typing is part of the code" is how I'd phrase it.
> I don't agree with this argument, I think it's akin to saying "I like driving blindfold because it makes me drive slower". You can use linters with limits on line length and number of params if this is a goal for you, no need to go indirect with the restrictions.
I was assuming this is more along the lines of writing clear variables names and writing functions that make it obvious what the return value is.
Also, the domain we're in is engineering. There are no single correct decisions and everything is about trade-offs. And that's good because otherwise our jobs would be the first to be automated out of existence. All of the discussion in this thread about people "thinking less of" other engineers for their opinions/experiences is fucking gross.
Also you only half-quoted me.
It's "... and/or on drugs".
Also people being stupid doesn't mean that I don't consider anything they say. Even a broken clock is right twice a day.
Yep. In my experience, hardliners tend to not be great engineers, even if they have decades of experience.
hardliners tend to not be great engineers
100% agree."Strong opinions, weakly held" is okay with me though. I don't call that "hardlining."
Although, sometimes it's hard to differentiate "strong opinions, weakly held" from "strong opinions, strongly held, not open to new ideas."
You must have a crazy good memory. I switch between multiple languages, and autocomplete lets me easily see what variant of a function this library uses again.
You know everything by heart?
> You know everything by heart?
You eventually reach that point. Do you still look down to see which keys you are typing?
This is also has the nice side effect of pushing you to use libraries that are stable and have good documentation as you can always reference them if need be.
Take for example the substring function. I wouldn't know which one it is for JavaScript, Haxe, ActionScript3, C#, Java, Python, C, C++, PHP. I know I used it at one point for all of them.
For languages like JavaScript, Java and C#, it's probably a method, so you can start typing. Might be substring(), might also be substr() or something like that.
For Haxe, I thought it was not a method so I think it's either part of Std or StringTools.
If I use autocomplete, I have it in a few seconds, and I can also see the documentation on the parameters. Is the second parameter an index or a length? To be honest I have no idea. And the good part is, thanks to autocomplete I don't need to know.
Also, if you work in a big codebase, you can't know every class. Needing to dig through code seems such a waste of time.
This is because you can remember where the keys are.
> For Haxe, I thought it was not a method so I think it's either part of Std or StringTools.
How would autocomplete help you here? If you need to know if it was a global function or else where? This is learned knowledge that is available through the proper docs and not through autocomplete. This is how you know not to look for a global function. The difference is that instead of reaching for autocomplete to try and fill in the gap, I go to the documentation. I basically share the same feelings/experience with this comment[0]. Either I know what I am doing and I do not need autocomplete, or I need to learn in which case autocomplete just gets in my way and I would rather go to the docs.
I know I am not alone in this, for example I watch the lead developer of pidgin, Gary Kramlich, on twitch[1]. He does not use autocomplete, any time he has to look something up, its straight to the docs.
Similar with Jonathon blow[2], who is working on his own game, game engine, and language who just uses emacs (with no autocomplete) and visual studio for the debugger.
And even Mitchell Hashimoto[3], who has a similar workflow of using a dumb editor and then going to the docs when needing to learn.
> I can also see the documentation on the parameters. Is the second parameter an index or a length?
The documentation on a per function basis does not contain enough information on how to use the library/framework. Take for example django's QuerySet aggregate function, would you learn enough from reading the function documentation here[4] on how to use it or from the actual documentation here[5]? Autocomplete wont give you anything close to the latter.
> Also, if you work in a big codebase, you can't know every class.
The classes you don't know are just one "goto definition" away.
> Needing to dig through code seems such a waste of time.
Needing to juggle through the autocomplete menu is a waste of time as you need to know _something_ for it be useful. It doesn't help you when you know _nothing_. I'd rather just go straight to the documentation or code.
---
0: https://news.ycombinator.com/item?id=37768953
1: https://www.twitch.tv/rw_grim
2: https://www.twitch.tv/j_blow
3: https://www.youtube.com/watch?v=rysgxl35EGc
4: https://github.com/django/django/blob/main/django/db/models/...
5: https://docs.djangoproject.com/en/4.2/topics/db/aggregation/
No I don't remember, it's automated, like riding a bike.
> How would autocomplete help you here?
Worst case scenario, I type "object.subs", wrong "Std.subs", wrong "StringTools.subs". Faster than you can bring up any doc.
> Either I know what I am doing and I do not need autocomplete, or I need to learn in which case autocomplete just gets in my way and I would rather go to the docs.
What about when you partly know, like substring?
Plus, how does it "get in the way?" You partly type it, and when you see it, you just press space or whatever completes it. Even in typing it's faster, because a good autofill even uses word distances for matching.
> who just uses emacs
There's the problem! ;)
> Needing to juggle through the autocomplete menu is a waste of time as you need to know _something_ for it be useful.
This is exactly it. Most of the time, you know something or at least can take a quick guess. If you fail there are still the docs. You and the other person are very explicit in "either you know it or you don't know it". For me, most of the time, I kind of know it. Probably the main difference is there.
Anyway, good thing nobody is forcing us to use anything, so we can both be happy in our own workflow :).
One question though: have you used Github Copilot and what do you feel about that? It would seem to me you would also feel it gets in your way.
In JavaScript, there are both, and a third one besides, largely for reasons of historical accumulation and inconsistent implementation:
String.prototype.substr(start, length?)
String.prototype.substring(start, end?)
String.prototype.slice(start, end?)
They’ve each got their strange nuances in behaviour (and, in so-ancient-you-certainly-don’t-care-about-them engines, cross-engine inconsistencies), so if you’re relying on autocomplete, it’s necessary that your autocomplete at the very least give some meaningful parameter names, because the difference between taking length and an end index is rather significant.Which one should you use? slice. It’s generally agreed to be the most reasonable of the three in what it does and how it works, and it matches Array.prototype.slice well. So: sorry that you thought it might be substring or substr, because those exist but you probably shouldn’t use them.
(Related reading: https://stackoverflow.com/questions/2243824/what-is-the-diff....)
Say what? Autocomplete is a godsend for quickly working with new APIs. If you're writing a function that takes some objects that you're not familiar with autocomplete can pretty quickly lead you down a good path.
Static typing makes it easier for my computer to get me the relevant docs.
This is very interesting and at first blush strikes me as backward. I wish I could sit in your office and see this happening, because I find it hard to imagine and I would love to know what that looks like. (And I have way less experience than you so I'm not saying it can't happen.)
Specifically, I find the domain-specific logic in my field is impossible to grok in dynamically typed codebases, while statically typed ones actually teach the developer what the business logic is.
> I may just be a grizzled greybeard screaming "the code _is_ the documentation"
Also confusing to me! In all my experience, static typing is what allows the code to be the documentation. Without it, there's no way to know what properties this object has or why we have a check for this certain property that I thought didn't even exist on this object. (Other than comments - maybe I'm on a weird team but I don't know anyone other than me who leaves comments of any significance.)
Good, long variable and function names, along with breaking complex expressions into multiple statements with those nice variables names can help the code itself describe the what of the process. And then static types help describe the what of the types of data even more.
That lets you save the comments for more useful things than an ad-hoc type system, like "// We need to do this because..."
I say that as someone that quite likes static types. I don’t care, do what you do!
Your comment pretty much echoes my opinion. Unfortunately in the problem space I work (data) Python is pervasive, but fortunately, it can be used in small isolated snippets, or Pyspark where this doesn't matter much.
For general large projects... I feel your pain.
> I find dynamic typing ... acts as a forcing function to writing simple code; simple to read and simple to comprehend.
I find the opposite true. Many super-dynamic patterns are hard to type correctly. Good type systems tend to encourage simpler patters so you get simpler types.
> The argument that it helps inexperienced developers approach the codebase is, IMO, a poor one as it tends to incentivize an iteration loop that lacks understanding and is simply trying to make all the red go away.
Making the red go away is important because the red indicates a problem! This is a lot easier than other ways of discovering the error. Why would you want to discover the error later?
> I also don't like autocomplete, so take that as you will.
This is why I'm with commenters who say they don't trust people who are against static typing... I'm extremely suspect of computer programmers who don't want computers to help them program.
I don't think that's what the parent to your comment is arguing. They are arguing that "making the red go away" isn't the goal, rather that correctness is, and that it's easy to conflate the too when you focus too much on the "red" part, and don't pay attention to the "correct" part.
Worded another way, the mantra of "if it compiles it works" can lead to a dangerous false sense of security if you don't understand the limitations of your type system and what parts of your program is may or may not cover completely.
Very few people have this mantra, much less without qualification. That's more of a ML or Haskell kind of point of view, and even then it's known to not be a guarantee. A type Int -> Int -> Int isn't going to enforce that you implement multiplication correctly, instead of add or just 0.
"I refactored, fixed type errors, and it just worked!" is a thing I see a lot, but from people who just experienced it, because it happens a lot. It's a good thing.
We have large python codebase, all sorts of linters and checks.
Still have developers do something like:
# we have a list of things
# but we only need one
try:
a_thing = things[0]
except IndexError:
pass
assert isinstance(a_thing, Thing)
Sure maybe it's a junior thing. But it still happens, and the typing doesn't save us. I don't know how to tell juniors to stop doing thisWhy is there an assert? Python case a cast function if you really wanted to force the typechecker to see a_thing as Thing but (and I'm sure this is the point you are making) you are likely hiding a prolem.
Watching typescript teams spent 30 seconds on the recompile is insane for me.
in fact, the whole ocaml family of programming languages are mega fast.
The most important feature of a very fast compile time is that you can load it with types after types and write in your whole mental model, without worrying about your build process slowing down.
That said, I agree 100% about the importance of fast feedback loops.
that's not to say that you can't write slow statically-typed compilers. but the strict syntax tree gives you much faster parsing/etc. dynamic languages can't avoid this either, it just does it at runtime instead.
and hot-reload for function changes/etc is very possible in a similar fashion to dynamic compilers too. And if you add a space or a bracket, because of the parsing problem, you can't even notionally contain that to a single part of the AST, the whole thing has to be re-parsed.
again, not that you can't make a static language compile very slowly, or that any given implementation is necessarily faster. but dynamic languages almost strictly have to do more parsing work, and have to do it more often, over larger parts of the codebase. and that's expected - static languages offload some of this "preprocessing" to the developer!
If this were broadly true for most developers using dynamically typed languages, it would be a very compelling argument indeed. But I think there’s pretty strong evidence the opposite is generally true: actual types produced to document real world dynamic code tend to be vastly more complex than the equivalent functionality implemented with static types from the outset. In the TypeScript ecosystem, DefinitelyTyped is an excellent source of countless case studies. The types they provide are typically definitely not, as you put it, “used in a simple way”. That’s not complexity inherent to the type system, or the type definitions as provided; it’s complexity inherent to the dynamic code they describe.
Equivalent packages which were statically typed from the outset tend to have much simpler interfaces because the types are defined upfront rather than retconned onto existing APIs. That doesn’t necessarily mean their interfaces are absolutely simple, but they’re typically relatively simple by comparison.
I’d go so far as to say that you can’t know how simple or complex an interface is without specifying it. If “the code is the documentation” (which I agree is a great ideal!), then interfaces without code specifying them are under-documented by definition. The further the code specifying the interface is from the interface itself, the more obscured your documentation is.
So when you say "if you know these rules nothing about dynamically typed code is inherently problematic", my intuition says "I probably don't know these rules". That goes for some of the basic rules like "the first argument to this function is the haystack, the second is the needle", but it also goes from some of the more complex rules like "functions that take a user ID can also take a user object and extract the ID from that" or "the allowed states for this FSM are X, Y, and Z, and are always written in capital letters". More importantly, a lot of the rules for my difference will not have been written by me, they'll have been written by my colleagues, or else they were written by me, but it was more than six months ago and my memory is a bit hazy on the details.
The point the previous poster was making, I think, was that while the rules may be very explicit (this is, after all, what any programming language is: explicit rules for a computer to follow), they can also be very complex. And, more specifically, the DefinitelyTyped examples show that the rules for dynamic software often tend to be very complex, or at least, complex enough to present difficulties when being modelled by a type system explicitly designed to model dynamic code.
I could not find good research that will decisively settle this dispute about static vs dynamic. So when you say "strong evidence" what are the sources for this strong evidence? Not asking to push against, I am gathering a list of articles and papers about this topic.
It’s possible there are papers on the topic, but I’m not aware of them. The evidence I’m speaking to is the complexity of the respective types themselves, ie either:
- those produced in the course of developing a given software package with static types as an explicit part of the development process, versus
- those produced post hoc to document the apparent interfaces of an equivalent package which has been produced without explicit types
One might call foul, by saying that this criterion favors static types by evaluating the complexity in terms of static types. But the reason I think it’s a fair comparison is because the dynamic interface does have a static type representation, even if it has to be conjured into notation post hoc… in much the same way as a data store model does have a schema even if it’s not formally specified as such.
This also happens with tests. It's easy to get wrapped in testing libraries and abstractions and spend way more time than you need to.
The 80/20 rule applies in both areas. You get 80% of the benefit with 20% of the abstraction. It's usually a mistake to try to go beyond this.
Of any popular language I'm familiar with, I'd say Go is the one that follows the 80/20 rule best. It encourages you to focus on solving the problem rather than leading you down rabbit holes of various kinds, including with types. That's not to say Go doesn't have its issues, but it really is excellent at discouraging over-engineering.
What? It helps developers inexperienced with a given codebase (not necessarily inexperienced generally), because you can very easily understand the types being passed into functions. You may need to understand why a function is behaving a certain way, so you look at the arguments, if you see a type you don't recognize you can push a button and your IDE will take you to the definition of that type. If you want to you can with another key combination see all the inheritors of that type, and so on. It makes it much easier to feel confident about what one can and can't do with any given object at any given time, which is helpful when one is getting up to speed with an existing breed of architecture.
Dynamic APIs are super hard to reason about if you don't have types to ensure you are calling your APIs correctly (function argument A can be number or string, but not formatFunction)
So having static types makes your APIs less dynamic because people are too lazy to type them correctly. Which in general is good in my opinion, making an API dynamic should be done only when it is really helpful to do so.
The other point is null-guards which _some_ (but not all) type systems enforce, null-guards are just 100% good thing.
I find dynamic typing similar to how unit testing is a forcing function for writing composable code, it acts as a forcing function to writing simple code; simple to read and simple to comprehend.
The argument that it helps inexperienced developers approach the codebase is, IMO, a poor one as it tends to incentivize an iteration loop that lacks understanding and is simply trying to make all the red go away. In fact I find that the type system is often a barrier to truly understanding what's going on as it effectively adds a very domain specific language to every project that has to be learned on top of the language itself.
There are methods to solving the problems presented in TFA that are just as robust as using types and which are simple to understand. Can types be used in a simple way? Sure. Are they ever? Not in my experience. I also don't like autocomplete, so take that as you will.
I may just be a grizzled greybeard screaming "the code _is_ the documentation", but perhaps that is born from my deep dissatisfaction with the current breed of get-big-paycheck-chatgpt-said-it's-right devs that are currently flooding the industry.