This isn't exactly the right place, but I don't know where else to ask this.
When people argue for static types (whether optional as in TypeScript or mandatory), one of the main reasons is "tooling". Why?
I know that writing a "go to definition" function for your IDE is impossible to do 100% correctly, when types are dynamic. But I would've thought it'd be pretty easy to do a 99% implementation, which rarely fails if you don't deliberately write code to fool it. Right?
Yeah, I think it might be a programming-language cultural thing. Most refactoring tools are written for Java and C#, so the people with experience writing refactoring tools have worked out how to solve the problems when they have certain assurances. But it really should be possible to write usable refactoring tools for ruby and python and javascript - you'll have to do a little more work to figure out name collisions between variables and methods and there's going to be ambiguous metaprogramming stuff like using strings to lookup instance variables, but having good tooling in the cases where you weren't using metaprogramming might encourage programmers to use a more concrete coding style, and that's probably a good thing. (Java has the same problems when you use a lot of reflection. So people don't use much reflection!)
YES! And I bet that a little bit of heuristics, a little bit of machine learning, and/or a little bit of user feedback (say, a button that tells the tool it went to the wrong function definition) would help such a tool cover a few more of the difficult cases.
This ought to be a classic case of "worse is better", which is why I'm surprised to see that people seem to shy away from it.
Think about this pseudocode; imagine it's The Hot New Dynamic Language Of The Week.
print "Which do you like better: cars or trees?
let a = AskUserForInput()
var b
if a=="cars" then
b = new Car()
elseif a=="trees" then
b = new Tree()
endif
b.DriveOnExpressway()
Clearly, that's not going to work out too well if the user picks "trees" but it's tough to spot before run time.
While that's a contrived and simplistic example (that you probably could somewhat easily detect with tooling/analysis) think about a language like Javascript where there are no classes and you may be adding or modifying a bunch of an object's methods at runtime. At coding time, the tooling doesn't even know if foo has a .bar() function, much less which .bar() function, much less if the particular .bar() function that foo may have is being called with acceptable parameters.
I was curious what PyCharm would handle that. Here's a screenshot: http://imgur.com/IvwG5
Basically it will complain if neither Car or Tree have a DriveOnExpressway method, but it's fine if one of them does -- the warning goes away if I uncomment the method. So there's ways to fall through the safety net, but lots of things it will catch.
(In Javascript, it's looser -- it only flags a potential problem if "DriveOnExpressway" isn't defined at all in the file.)
Ok, if I understand you right, your point is that a tool like a linter will fail to warn about this error. I'm not sure what current (say) Python linters do in situations like that.
But a tool like "go to definition" would work fine, wouldn't it? Maybe you would need a heuristic that guesses what are the possible types b could have, and detects that Tree doesn't have DriveOnExpressway, so it takes you straight to the definition in Car. Certainly, there are more complicated cases where guessing b's type is harder or impossible. I'm not denying that.
Because there is no type. It's dynamic. You can't figure out what the type is unless you run the code.
def do_something(thing_a, thing_b):
...
What's the type of thing_a or thing_b?
There is none. There isn't even a type globally. thing_a and thing_b acquire new types at every call instance. This basically screws you for all useful auto-complete information.
You can't even do proper go-to-definition because of things like getattr.
Yes, there most certainly is a type. It's dynamic, but not nonexistent. Now, of course you don't know the type for sure unless you run the code. I said so at the start. But I'm saying, can't you do a pretty good job in most cases? In this case, you could go to all places where do_something is called and see what objects are being passed in.
Yes, maybe there are situations where dozens of different types are passed in by different calls, or where we're building a library and we don't have any calls to do_something in our codebase, and cases where everything is obfuscated by one means or another. But depending on style I bet there are lots of cases where it works out ok.
It is just much more difficult to have tooling since the tools cannot know for sure what the program is doing (and neither can another human looking at it).
To be more specific you have to use some crazy static analysis technology (global interprocedural analysis) that is intractable unless you sacrifice accuracy. Human have similar problems as compilers/tooling, though they are a bit better at understanding nuanced conventions to make better judgements about what dynamic code is doing.
Yes, of course, we sacrifice accuracy. I said so at the very start. But imagine a "go to definition" tool that works correctly for 99% of cases and barfs or goes to the wrong place on the others. If the alternative is no tool at all, that still sounds pretty useful!
I admit there are cases (refactoring is an example) where if you don't trust the tool 100% you won't use it. But many tools are not like that.
Well, you'd be without feedback alot, state of the art in type recovery is nowhere near 99%. Your libraries can exploit the flexibiliy of a dynamic language (meta programming, crazy unions) and make the job much harder. Typescript seems very interesting in the way they handle type retrofits on libraries, but I haven't really absorbed it yet.
I came to ask a similar question -- how much better is IDE support for statically typed languages than, for example, JetBrains' IDE support for Python, Javascript, CoffeeScript, etc? They offer code-completion, live error highlighting, go-to-declaration, refactoring (with a confirmation step if it's ambiguous), all that good stuff.[1] The interface asks for clarification when it's not sure what something refers to. It seems to work pretty well. I don't really use typed languages, though, so I don't know how much I'm missing. Can anyone comment based on experience with both?
(I actually also came to say, damn, y'all, IDE support is awesome, and it's here for dynamic languages. Go-to-declaration alone is lifechanging, since it's usually faster to jump into a library call with ctrl-B than to check the docs. Live error highlighting alone is worth it. Just give JetBrains whatever they ask for.)
If you've ever used Resharper then it's difficult to work in any other environment. I doubt most of Resharper's features would be possible in a non statically typed language. It's a Jetbrains product as well:
When people argue for static types (whether optional as in TypeScript or mandatory), one of the main reasons is "tooling". Why?
I know that writing a "go to definition" function for your IDE is impossible to do 100% correctly, when types are dynamic. But I would've thought it'd be pretty easy to do a 99% implementation, which rarely fails if you don't deliberately write code to fool it. Right?