Writing scientific/academic code and writing business application code is somewhat different, but the speed at which you can develop and maintain Python code is very high, as is the volume of aid available should you run into trouble.
I don't think there's a language out there that's both easy to initially pick up and as well supported by its community as Python. The speed at which you can develop application code is astounding, once you get the hang of it and the noise out of the way (CI/CD, code coverage, unit and integration testing, a general familiarity with the major frameworks, etc.), and the maintenance is... tolerable.
Personally, I use Python as a Bash replacement a lot, that's what I think of when I hear "short scripts." Sort of what people used to use Perl for. Pushing strings around, complicated repetitive filesystem manipulations. (And data sciencey stuff of course.)
Python it is! If it doesn't already have something in the standard library for your task, it's definitely in the PyPI. This reduces your work to importing a library and writing 5-20 lines of code.
Used to? People still do this (I should know; I'm one of them).
Static analysis tooling is excellent and type annotations only aid their accuracy.
It's my impression that developers tend to learn to love static typing if they didn't cut their teeth on a statically typed platform. It takes experience to know that certain errors can be mitigated via language features and to know how annoying those errors are when they needlessly pop up.
Unenforced explicit typing was and is still a boon for Python. Typing is often lost on developers who are still in "Hello, world!" territory and scientists who are using it as an adjunct to MATLAB. It can be a hindrance to rapid development, as well.
I'm not alone in thinking that, the creator of Python himself has devoted his recent work to adding types to Python. Is that just because he doesn't write his code "the correct way"? Does he simply know the syntax but not hoe to effectively use the language? I don't think so.
1. Love that it doesn't use structural typing, NewType seems great.
2. The syntax is bad. Maybe this is a "it just takes getting used to" thing, but I actually find it really bad. In TS, the syntax for typing almost always directly matches the syntax for the rest of the language. In Python, its a weird sort of LISPy DSL think that they made... compare:
Py: Callable[[List[Tuple[int, string]], Dict[string, string]], int]
TS: ([number, string][], { [key: string]: string }) => int
This only gets worse as you chain callables together, whereas in TS everything left-associates as you'd expect and it all works out nicely.3. Admittedly, the TS dict syntax isn't beautiful, but it becomes very helpful for things like `{ [K in keyof T]: K extends number ? T[K] : never }`, which does not seem to be possible in Py.
4. Related to 3, but no never type? I see NoReturn, but it does not seem to actually cause any errors when you try to assign it to a variable. See below.
5. Type narrowing... does it exist? Seemingly not, see below.
6. Literals as types (enums)?
7. Generics, do I really need to pass in the internal representation of the type I'd like to use? That seems absurd. Will bad things happen if these internal identifiers collide? (Reference: `T = Generic('T')` creates a generic type)
Demo code that should throw an error at the assertUnreachable and nowhere else, but actually throws errors everywhere but the assert unreachable (types seemingly aren't narrowed by `type() == ...` checks):
def foo(x: Union[str, int, float]):
if (type(x) == int):
return x / 3
if (type(x) == str):
return x.upper()
return assertUnreachable()
def assertUnreachable() -> NoReturn:
raise RuntimeError('no way')
(this is all checked using mypy, v. 0.641)When you say type narrowing do you mean floats should be automatically interpreted as int? There is typing.{SupportsInt, SupportsFloat} which can be considered "number" base classes which you may consider as type narrowing. Otherwise if you mean "type of x is known in this if-block" you do get that with `isinstance` type checks (which is the preferred, pythonic way).
I agree that mypy should warn if a NoReturn is assigned; apparently it just ignores typechecking below the NoReturn function call, and is really only used to ensure the NoReturn function is guaranteed to raise before it returns.
3 and 4 are disappointing though.
0: I'm assuming the extra [ after Callable is a typo.
The extra [ is not a typo, the syntax is: `Callable[[Arg1Type, Arg2Type], ReturnType]`. Or, if the arguments don't matter, `Callable[..., ReturnType]`, but this does not mean that the type is not a valid expression.
Edit: I was missing a ] actually, separating the arguments from the return value.
Well somewhat[0], but the main putative benefit would be having types be valid expressions in the target language, since you can do type-checking with function decorators, and generally interact with types useing the normal language mechanism for interacting with values, rather than some horrid bolted-on piece of crap like C++ templates.
Server = Tuple[Address,ConnectionOptions] # array subscipt might not the best choice here, but it works
UserId = NewType('UserId',int) # ordinary function call
Scalar = int | str # could be equivalent to Union[int,str] with appropriate value of Type.__or__
0: for example: def intBit(N):
if N==0: return type(None)
if N==1: return Bool
if N>INT_WIDTH: return long
return int error:invalid type comment or annotation
note:Suggestion: use intBit[...] instead of intBit(...)
So what's really happening is the type expressions are pretending to be "just everyday python", but actually they have arbitrary restrictions (cannot be functions? need to work via overriding `__getitem__`?) that neither you nor I were aware of. This is probably the least "pythonic" implementation possible.And even if the code were valid, it's relying on N being a statically known value, which is a bit off because sure, you could have N be some global const config variable, but it would be very weird for configuring the value of the variable to require you to also go into the code and change things around to work with bool's or None's instead of int's.
Yeah, that sounds about par for the course for bolt-on static-y typing in languages that aren't supposed to be statically typed.
People say that it's good for "short scripts", but whenever I decide to write something in Python instead of TS, I'm instantly met with so many runtime type errors that I wonder how people can honestly believe lack of static typing increases productivity in these "short scripts". For instance, the second I decide to refactor, I know that even once I think I've cleaned everything up, the next few minutes will be spent running the code a few times to flush out all the type errors.
Python is not my primary language, so that could play a part in the issues I have, but if I am allowed to lose humility for a second or two: even though I primarily use TS, my Python is still, in my opinion, stronger than many of my (college student) peers. I hate to imagine all the issues a novice would face.
To be fair, I haven't used mypy in a while. I remember that being decent, but nowhere near the level of TS, in terms of both the power of the type system and the editor support.