For four days, I spent debugging a python production script because in one place I had typo'd ".recived=true" on an object and just couldn't understand why my state machine wouldn't work.
And very quickly, the whole team became fans of __slots__ in Python.
I still write 90% of my useful code in python, but that one week of debugging was exhausting & basically wouldn't have even compiled in a statically declared language. Even in python, the error is at runtime, after I got the __slots__ in place.
nothing stops you from using static typing with python3
The typing spec provides for supplementary "stub files" that can be provided for third-party dependencies without their own annotations. The typeshed project provides these for a fair and quickly increasing number of common dependencies, and is plugged into pycharm and mypy by default: https://github.com/python/typeshed
Can we stop pretending that adding type annotations to a 'dynamic' language solves the "good static typing" problem? It's just silly.
(About as silly as pretending that static types solve all problems.)
Is that really a dynamic typing problem or a language that allows you to create instance members anywhere? It seems like that is a flaw in the declaration model of the language and not a static / dynamic issue.
$ txr
This is the TXR Lisp interactive listener of TXR 185.
Quit with :quit or Ctrl-D on empty line. Ctrl-X ? for cheatsheet.
1> (set a.b 3)
** warning: (expr-1:1) qref: symbol b isn't the name of a struct slot
** warning: (expr-1:1) unbound variable a
** (expr-1:1) unbound variable a
** during evaluation of form (slotset a 'b 3)
** ... an expansion of (set a.b 3)
** which is located at expr-1:1
Both warnings are static. If we put that into a function body and put that function into a file, and then load the file, we get the warnings.The diagnostics after the warnings are then from evaluation.
Those are nothing; TXR Lisp will get better diagnostics over time. I'm just starting the background work for a compiler.
There is dynamic and then there is crap dynamic.
Don't confuse the two.
There is crap static too. Shall we use C as the strawman examples of static? Hey look, two argument function called with three arguments; and there's a buffer overrun ...
Excellent. My point exactly. I have no fear of using a static or dynamic language, as long as it is a good implementation of a statically (or dynamically) typed language.
Good "dynamics": All the Lisp-family languages. Smalltalk. Julia. Lua. Tcl.
I mean, we can argue the semantics of what, exactly "static type checker" means, but...
A "static language" occurs when we have a model of program execution that involves erasing all of the type info before run-time. Or most of it. (Some static languages support OOP, and so stuff some minimal type info into objects for dispatch.)
Note how above, my expression executes anyway; the checks produce only warnings. The warning for the lack of a binding for the a variable is confirmed upon execution; the non-existence of the slot isn't since evaluation doesn't get that far.
If we retain the type info, we have dynamic typing. There is no limit to how much checking we can do in a dynamic setting. The checking can be incomplete, and it can be placed in an advisory role: we are informed about interesting facts that we can act on if we want, yet we can run the program anyway as-is.
For example, these days it's quite possible to ask GHC to defer type errors to runtime. Does that mean that the GHC dialect of Haskell is dynamically typed? This is basically a command line switch away, btw.
Retention of type information does not "dynamic typing" make. As a trivial example, consider C++ RTTI.
You really have just reinvented static (type) checking and a good runtime. There's no shame in that, but let's not pretend that these are opposing forces.
I still think you're just arguing semantics.
EDIT: Incidentally, the statically typed crowd can even go the "other way", namely from runtime -> compile time. For example, it's quite possible to derive a static proof/type from a runtime value in e.g. Idris by pattern matching as long as you're meticulous about building up the proof.
Usually I use normal keywords for throwaway or glue code, but anything important (my actual domain entities) will be namespaced, allowing (relatively) pain free refactoring.
Cursive has a few good refactoring tools/shortcuts, but I would also like to see extract/inline/move for functions.
Its not a huge problem, just something I miss from the static languages.
Not just, "who the hell uses this", but "where the hell is this defined" as well.
On Common Lisp, a dynamic language, I can also get this answered instantly. I just press a key combination on a method call and i jump to the definition.
So this isn't exclusive to statically typed languages.
It is not. A language feature is either amenable to static analysis (not necessarily type checking) or it is not.
> Consider dynamic class loading in Java, dlopen / reinterpret_cast in C++ etc.
This actually emphasizes my point. Features that are not amenable to static analysis are problematic for tooling.
How can it be a niche language, if it's an ANSI standard, has more than 8 or 10 fully feature, standard-conformant implementations, runs on most CPU types, and has been proven to work in production systems for spaceship guiding, worldwide airfare reservation and credit card transaction verification?
You use Js and Python because you choose to use it, but it's not the only choice. Not only Common Lisp, you could also be working in Clojure with many benefits.
Maybe good tools are able to perform some static analysis and rule out some of the methods with the same name but impossible types, but the language doesn't rule out situations where the best the tool will be able to do is show you all of the function definitions with the same name as the (dynamically dispatched) function call site you're looking at.
Let's say you have seven different type hierarchies having dynamically dispatched functions named "run", with 5 definitions in each hierarchy, for a total of 35 functions named "run". In a statically typed language, if the code compiles, it's possible to narrow down the type for a given call site to either one of the hierarchies or one definition, meaning you have to look at either 1 or 5 definitions. In a dynamically typed language, there are situations where legal code results in the tool having to throw up its hands and show you all 35 definitions.
The flip side is that if you really have a spot where you'll need to dispatch to any of the different hierarchies, then in a strong statically typed language, you'll need to either create an algebraic sum type covering all 7 hierarchies, or you'll need something like a typecase / typeswitch statement to enumerate out your possibilities.
And even in a statically-typed language there will be cases where the tooling can only determine fairly generic things statically.
I don't see anyone advocating for abandoning static typing over that occasional limitation. Yet I do see people proposing similarly-infrequent issues as cause to abandon dynamic typing.
When $problem happens in $language_I_dislike, it's a clear sign that the language itself is inherently broken.
Well, usually. It gets a little confused when you start using dependency injection containers, or dynamic requires, or anything like that.
And that's really all I care about, I'm not about to start writing production code in Lisp.
And the other way around: Statically typed languages without module systems, such as C, exhibit this problem too.
The 99% stat is quite likely a big exaggeration.
Refactoring is far far harder to do reliably, and more straining as so much more responsibility on the programmer.
You would think by looking at the code that the creators had a 30 word vocabulary, because 80% of the code uses the same six nouns and four verbs to pass data around and what you use those for depends on the context of who is calling it.
Oh, but the entire thing is written using promises, so most of your function calls have no context. It's hell, and I'm starting to worry that Node has dug itself a reputation hole it will never get out of.
In smalltalk the parameter names are part of the function name to reduce the likelyhood of name clashes. You still have the same problem when you have two functions with the same name and parameters.
Nobody ever talks about that little horror when they bring up how Smalltalk could to refactoring JUST FINE without static types. It wasn't just fine, turns out.
I think the best thing I've found for this personally is coccigrep, which works but I've only used it a couple of times. I'd like something I'd reach for about as often as I reach for grep. (Also I think these days you'd want it to be based on clang or something.)
One thing that does seem to be true is that the requirement to name types means that a textual grep is way more reliable than it is in C. If I want to find all places where a Python class is used in a large codebase, I might as well give up.
Also they are backed by Clang and muuuuuuuuuuuch faster than in Visual Studio + whatever paid extension
A very nice feature it has and that I didn't see elsewhere is the optional case-sensitive renaming. eg renaming Foo to Bar will also change foo to bar and FoO to BaR.