Type inference might got one halfway there, but it would work best at the call site -- the type of variables could be inferred from return values and from literals and so on. But you'd still want to define a function as basically returning "Any". This means a caller wouldn't have a type for the return value, and so if you want to do "fileName := funcReturningAny(); openFile(fileName)" and openFile() was defined only for a string argument, the compiler would have to generate a runtime check at the call site to assert the type fileName as being a strong. This the call site is penalized for being typeless, but the statically typed code wouldn't be.
I suspect this would work pretty well. You can start out with no types, and you'd be able to add types as you go, slowly tightening the screws on your program. Generics, traits and other modern abstractions would still be available, though some of those features would start to become unavailable or awkward to use with any code not sufficiently tightened with types.
I don't know of any language that does anything like this, although I suppose TypeScript is pretty close.
> sub takes-string(Str $foo) { }
sub takes-string (Str $foo) { #`(Sub|94327719210888) ... }
> takes-string(5); say "Hi!"
===SORRY!=== Error while compiling:
Calling takes-string(Int) will never work with declared signature (Str $foo)
------> <BOL>⏏takes-string(5); say "Hi!"Most programming I've done in the past has been with python (no type hinting) or languages with weak static type systems (java et al). Having to verbosely specify the types in java was always just painful, while the upfront speed and freedom of python felt much nicer. However after getting a taste of better type systems by dabbling in rust and elm, now every time I write something in python I'm thinking "was argument a string or a foo class?" or "that runtime error would not have happened if the type checker caught it".
Python type hints now are a huge plus for the language. It still seems a bit "hacky" though. I'd love to see something that adds more syntax sugar and feels more built in. Maybe it's just me.
Function arguments do benefit from clear inputs and outputs (is this type X? a list of X? A list of anything? Is it returning Y? List of Y? Nothing at all? An error or nothing? Something that is just like X but with an extra field?)
Inside the function? Leave me alone. You are a computer, go figure out. Which seems to be the direction we are heading towards.
> was argument a string or a foo class
Oh, it was supposed to be a string. However, it is a string that came from the UI, so it is untrusted, so your SQL function should refuse to use it. But hey, feel free to lowercase it.
The main problem is that usually type systems are not very rich, so the baby gets thrown out with the bathwater.
Also, runtime errors are ok if they happen in your test cases. You do have them, right? :)
Yes, but if some errors can be caught sooner by your linter/IDE/typechecker then it speeds up development. Would you rather be notified of a type mismatch immediately after saving the file or later when you run the test suite?
Idris is what's bringing me back to typed land.
[1]: https://flow.org/
No they don't; even in C it's possible to just type everything as void*, and everything as Object in Java. Just nobody does this 'cos it introduces a bunch of unnecessary runtime errors.
https://stackoverflow.blog/2017/09/06/incredible-growth-pyth...
example of something annoying that came up:
foo = [1,2,3]
bar = {'1': 1, '2': 2, '3': 3}
for i in foo:
print(bar.get(i))
What does it output? None x3. In order to get it to do the thing, I did something like this: foo = [1,2,3]
bar = {'1': 1, '2': 2, '3': 3}
for i in foo:
str_i = str(i) # even more fun when this throws
print(bar.get(str_i))
I use python because most of my coworkers understand it and it has wide library + runtime support where I work, but it's definitely not my favorite thing to work with. Dynamic typing where types still matter and where the language is picky about implicit type conversions kind of sucks.I also couldn't do:
foo = [1,2,3]
bar = {'1': 1, '2': 2, '3': 3}
for str(i) in foo:
print(bar.get(i))
Which was also eyeroll. Like, I get it, but...c'mon. Working with Pandas is also more or less learning another language, with the insanity that is df.T.whatever and df.loc[]: df.loc[df['shield'] > 6, ['max_speed']]
I'm sure that someone will comment about how I'm an idiot and there's a much better way to do it, but given PEP20: There should be one-- and preferably only one --obvious way to
do it. Although that way may not be obvious at first unless
you're Dutch.
I guess I'm not Dutch enough.I don't have a problem with that - it just seemed kinda incongruous in that context. I imagine the other critics would be even more unhappy about implicit type conversions.
>> bar = {1: "a", "1": "b"}
>> bar[1]
"a"
>> bar["1"]
"b"Why not just do the following?
foo = [1,2,3]
bar = {'1': 1, '2': 2, '3': 3}
for i in foo:
print(bar[str(i)])I could write an "obvious" fix for your example, but I expect that it'd be unsatisfactory, since the real problem likely has a different solution.
It looks like you might want a little utility to transform key lookups to a standard. Web frameworks do stuff like that for mapping URLs to functions, to ignore case, etc.
You might be interested in learning about the ``dict.__missing__`` magic method.
bar = {1: 1, 2: 2, 3: 3}I find this sort of generalizations off-putting. It is all about trade offs.
Types can be very useful. There are whole categories of errors that are avoided by types.
And yet, in many cases they are a productivity drain. Time is often better spend by writing testcases instead of chasing down type issues(more so if a language has a 'generics' or C++ templates concept), so that approach got favored in many fields. These will catch type errors, and static languages also need tests, so the gains are not so easily quantifiable.
It is not just a matter of ergonomics, it is a matter of getting out of the way of the problem domain. Most people are paid to solve actual issues, not to craft convoluted C++ templates, custom types or deep type chains.
Type inference helps, but even Haskellers will often avoid it when defining functions, so that they will be explicit. But Haskell has incredibly powerful constructs. I was amazed that you can actually tell the compiler you are writing a recursive data structure[1], which is something that most languages won't allow you to do – the most they will allow you is to define a reference to the same type of object(or 'struct'), and you have to stitch that together in runtime. Simple cases are also simple. A function that gets a list and returns an element of that list? [a] -> a, I don't care what a is, nor does the compiler. But feed it something that is not a list, and it will complain.
A language should let you easily define new types, and allow these to be used without friction by standard functions. For instance: if I have an integer, is this integer describing a temperature value? If so, functions that work with speed should refuse to operate with this data. Standard mathematical functions _should not care_ and should do whatever operations we request(without any silly type casting, I told the compiler this thing is a number, use it). The compiler optimizer should also use this information to generate appropriate assembly instructions.
So yes, if you have an expressive type system, types can be a joy (you'll find yourself thanking the compiler often). If instead your type is spent trying to figure out why IEnumberable<Something> cannot be converted to IEnumerable<SomethingElse>, even though they are subclasses and even though the same operations work with arrays, then types are getting into the way of solving real problems[2][3].
And even with an expressive type system, there is a non trivial cognitive cost. Sometimes you just want to write (map (lambda (x) (do-something x)) lst) and not really care what it is that you are manipulating. On the other hand, I personally do prefer using Golang to talk to AWS apis (instead of python or javascript), precisely because of their deeply nested type hierarchies.
Being able to sprinkle type annotations where it is useful and have the compiler figure out as much as possible otherwise is a good compromise, which is the approach that the Python article talks about.
I would read this as not favoring mandatory static type checking in 2018 but, instead, using a hybrid mechanism that benefits from both approaches.
[1] http://tuttlem.github.io/2013/01/04/recursive-data-structure... [2] https://blogs.msdn.microsoft.com/rmbyers/2005/02/17/generic-... [3] http://www.baeldung.com/java-type-erasure
Assuming an equivalent program written in a statically-checked language and a dynamically-checked language, if your program has type errors at compile time, it will also have type errors at runtime. The difference is when you know about them.
Static type checking is (informally) about statically proving that the domain of your function is actually what you think it is. They catch "obvious" (for a definition of obvious that is defined by the power of the type system) domain errors. They don't replace tests, but they do constrain the domain you need to test and let you know when your tests are sane.
From my experience I've seen that type hinting and checking provides a better ROI than automated testing. It is not either/or, you will most likely need some automated testing, even if it is just unit tests. But at the very least use types. It provides broader benefits than just catching stupid bugs and typos. At makes your code base much easier to understand and code paths much easier to follow.
If you actually use the type system, and build less anemic types, you can eliminate huge swathes of potential errors.
I think the debate is largely going in favor of static type checking. The widespread adoption of type inference in many statically typed languages (var in Java, auto in C++, let in Rust) has greatly eased the ergonomics of using a statically typed language.
Unless there is a massive ecosystem that you need to take advantage of (for example javascript), in 2018 greenfield development should probably be in a statically typed language.