This would make python both a viable choice for rapid prototyping, as well as easy to convert to much higher performance code (just by handling some typing conflicts), and potentially making python a mega-language used even more pervasively.
This would make python both a viable choice for rapid prototyping, as well as easy to convert to much higher performance code (just by handling some typing conflicts), and potentially making python a mega-language used even more pervasively.
https://github.com/adsharma/py2many/blob/main/doc/langspec.m...
The tool can convert your code to C++/Rust/Go and give you a native binary which like you observe will run faster without the python runtime.
There are many open tasks that could use help.
Py2many is a transpiler, which converts source code of one language to source code of another language (which then needs to be compiled).
So far, Py2many seems to support Rust and C++14, and preliminary support for Julia, Kotlin, Nim, Go and Dart.
Py2many tries to primarily build a standalone C program without the python runtime, although it supports a --extension flag, where it generates a PyO3 extension for rust.
If you use additional type markings it can generate more performant C code.
BTW: I remember a blog article of someone who was using Cython as the primary language instead of Python.
As the sibling comment notes, transpiler is not a very useful term.
A thought assembler was a "compiler", where a "compiler" is a special type of transpiler that is only capable of producing machine code.
There are also many, like me, who find it to be a distinction without a difference. We call them all compilers.
One example is the TypeScript team:
> Let’s get acquainted with our new friend tsc, the TypeScript compiler.
https://www.typescriptlang.org/docs/handbook/2/basic-types.h...
Another is Bjarne Stroustrup. Back in the glory days of BIX, I posted a comment in one of the forums that said, "C++? That's a preprocessor, isn't it?" (At the time, the only C++ implementation was Cfront, which translated C++ to C.) Bjarne replied in no uncertain terms that Cfront was a compiler.
Longer version of the story with some discussion of other words and phrases:
https://news.ycombinator.com/item?id=15154994
Of course, the word "transpiler" had not yet been coined, but I have a feeling Bjarne still prefers to call Cfront a compiler, as the Wikipedia page does:
> Cfront was the original compiler for C++ (then known as "C with Classes") from around 1983, which converted C++ to C; developed by Bjarne Stroustrup at AT&T Bell Labs.
https://en.wikipedia.org/wiki/Cfront
So to my mind, a compiler doesn't have to generate a "compiled executable". It also doesn't have to take "source code" as input.
Most implementations of JavaScript and Java have at least two compilers: one that translates source code into bytecode, and a Just In Time Compiler that never sees the source code, but analyzes and profiles the bytecode while it runs, and translates sections of the code into machine language as it finds hotspots.
Now if you like the word "transpiler", as many do, I won't try to convince you otherwise. I just wanted to explain why some don't find it a useful distinction and prefer "compiler" for all these cases. It's a program that translates computer code from one language to another, whatever the form of those languages may be.
I suppose by my own argument I should also call an assembler a compiler! But no one does that... :-)
Friend fyi... the word "transpiler" to make a distinction of converting the source to another higher-level language rather than a lower-level machine language appeared as early as 1960s. In the 1964 paper, see the last page and the 2nd-to-last paragraph:
http://comjnl.oxfordjournals.org/content/7/1/28.full.pdf+htm...
>There are also many, like me, who find it to be a distinction without a difference. We call them all compilers. [...] Now if you like the word "transpiler", as many do, I won't try to convince you otherwise. I just wanted to explain why some don't find it a useful distinction and prefer "compiler" for all these cases.
I understand your point of why "compilers" already encompasses transpilers so the word "transpilers" seems redundant. But I'll try to explain why "transpilers" still endures. If you're not familiar with concept of "lumpers vs splitters", take a look at: https://en.wikipedia.org/wiki/Lumpers_and_splitters
Imagine if we did not have the word "transpiler". Language usage would still evolve to make a distinction via extra prefixes or suffixes or extra adjectives. Examples of alternative history might be:
- "compiler-to-asm" or CTA acronym, or "compiler-to-executable", or "native compiler"
- "compiler-to-another-high-level-language" or CTAHLA acronym, or "programming language translator"
But humans don't like to say or write verbose multi-syllable terminology and they'd eventually substitute a simpler word as shorthand for the distinction. That shorthand word might be "transpilers" or another word to serve the same purpose. And that's what happened in 1964 when AF Parker-Rhodes suggested the word "transpiler".
Similar concept of labeling Linux ext4, Microsoft NTFS, Apple HFS as "file systems" instead of just "databases" -- even though they are all conceptually "databases" which have same computer science concepts of keys, blocks, btree indexes. If we tried to police the language and insist that "file system" adds no useful distinction because it's a "database"... human language usage would still evolve with extra adjectives such as "operating-system-built-in-database-for-documents-and-arbitrary-files-etc" ... which is a cumbersome mouthful which motivates a shorthand terminology... perhaps call it "file system".
Now, my dream would be for Python to actually enforce type annotations at runtime when they exist. Maybe add some kind of syntax to avoid doing it when it's expensive, e.g. `list[nocheck int]`, but for the love of god, enforce it, otherwise I feel like I'm just peppering my code with limp suggestions that only stand if some third party package can find all paths that lead to them, at which point I'd rather use a true statically typed language where the whole system doesn't look and smell like a janky afterthought.
Finally, CPython is very old. That means it made decisions around multithreading it still has to live with to this day whereas the JavaScript implementations have always been single threaded and added multithreading much later (learning from the experiences of other languages making the transition).
Dynamic typing is part of the problem but you’re right that it’s not the whole thing.
The bigger thing is that the Python runtime has a GIL because that's kind of how you solved the problem when multiple cores started becoming mainstream in the 90s. The Linux kernel had a similar GIL. It's an easy way to technically support running in a multi-threaded environment while reducing maintenance issues at the cost that you can never use more than 1 core at a time. JS runtimes don't have this problem because the runtime support layer is almost non-existent & there's no multi-threading support at all (indeed, V8 by itself comes with almost no JS APIs - there's a clear delineation between language runtime & "app runtime"). Service workers is extremely thin & works on channels instead of shared memory, further simplifying the design. The JS GC is typically written to support concurrent multi-threaded operation to minimize the "stop the world" phase runtime whereas Python's cycle detector needs to stop the world due to the GIL. These are all small decisions that add up.
https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
Which I've found extremely usefulProperly statically typed languages do not need run-time checks because the compiled code is already proven to be valid by the type-checker.
Okay, so there are some, though flow is pretty obscure, and flow-runtime (which is the part which does this AFAICT) is even more obscure. I don't think this is a popular feature.
> Properly statically typed languages do not need run-time checks because the compiled code is already proven to be valid by the type-checker.
If you want that, just enable strict mode for mypy.
And if you want to interface with code that is not stirctly typed, just use runtime type checks, which will inform the static type system:
foo: Any = untyped_code()
assert isinstance(foo, MyClass) # after this mypy will treat foo as instance of MyClass.
Same can be done with other types of checks also.So if you want runtime type checking that informs the type system, you have it. I certainly don't want runtime checks for what the type system has already checked for me.
But I can't do that if I want to do gradual typing. If I type a function, but the type system cannot figure out what's going to call it, that's when I want it to perform a check: at the boundary.
> I certainly don't want runtime checks for what the type system has already checked for me.
The type system can remove the checks if it can prove that they always pass. My point is that if it cannot prove that they will pass, I want them at runtime.
Unless you start using # type: ignore - the only way to interface with untyped code will be to use asserts or other checks (if + throw), i.e. perform checks at the boundary.
Well, what's a properly compiled language these days? Golang e.g. uses run-time dispatching due to the way interfaces work and, of course, garbage collection. Even C++ needs to do run-time dispatching of virtual methods. So the only languages left these days that do not use run-time checks are probably C and Fortran.
For example, in JS you might put these all over your code-base:
if (typeof x !== 'string') {
throw Error('Expected a string');
}I e said it before and I’ll say it again: runtime type checking saves you from the case where you pass None instead of an actual object but not much else. How wrong would your program have to be to pass an instance of Animal instead of an instance of BankAccount? And how quickly would it error out with “giraffe does not have an attribute routing_number”?
Does any mainstream language do this? For sure most don't, even statically-typed ones. I'm all for enforcement of types, but why does it need to happen at runtime?
The use case for mypy is strictly for static type checking AFAIK.
I'd argue a good JIT compiler will always beat a static compiler, especially for a dynamic language using a complicated syntax such as python.