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)
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)
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.)
std::vector<Pair<std::string, MyInt>> s;
s.push_back(Pair<std::string, MyInt>("a", MyInt(1)));
s.push_back(Pair<std::string, MyInt>("b", MyInt(2)));
s.push_back(Pair<std::string, MyInt>("c", MyInt(3)));
Gross. I'm being punished.Look at this Rust code:
let mut s = Vec::new();
s.push(Pair::new("a", 1));
s.push(Pair::new("b", 2));
s.push(Pair::new("c", 3));
Both are statically typed, but one uses inference. It practically feels like Python everywhere except where type declarations matter most to me -- function signatures: fn foo(x: f64, y: f64) -> Point {...}
In Python, those aren't very helpful. def foo(x, y): ...
:c std::vector<std::pair<std::string, int>> s;
s.push_back({"a", 1});
s.push_back({"b", 2});
s.push_back({"c", 3});
I mean let's compare apples to apples here.That IS better than Rust since it migrates the type from n container members to just the 1 container. It doesn't seem possible to replicate that in Rust[0].
My Rust evangelism IS hurt by this. But its HP bar is big[1].
0: https://news.ycombinator.com/item?id=10891892
1: https://www.reddit.com/r/rust/comments/bya8k6/programming_wi...
Also, being a C, you still have to declare* all the types, even when only `id`, which is tedious AF. Also-also being a C (which doesn’t so much have a type system as a loose set of compiler directives for allocating memory on the stack), it’s not as if declaring exact types does much to improve program correctness. You can still stick any old object into an `NSString*` at runtime; it just dumps stack when you try to send it an NSString-specific message is all.
Oh, and one more: the ObjC compiler can have trouble recognizing selectors (method calls) when the receiver’s type is `id`. Being a C, obviously you have to import all the headers just to use them at all, but if in searching those headers it finds two method definitions with the same name but different parameters then its superficially Smalltalk facade quickly breaks down. (I’ve run into this, it’s very annoying.)