Hacker News new | ask | show | jobs
by timothycrosley 2437 days ago
More and more I want someone to create a new language that amounts to a strict subset of Python, with mypy built-in, and is compilable into machine code. Python has by far my favorite syntax, community, and in my experience leads to the greatest productivity. There just happens to be a lot of overly dynamic features, that aren't even used by most, but used just enough to hold back optimization and structural improvement.
15 comments

https://github.com/python/mypy/tree/master/mypyc

This may interest you. This is probably going to be the 'official' way to get what you're talking about.

Thank you for sharing! This looks really promising, I'll try to think of ways I can contribute to the project.
yeah, I'm excited about this approach. but it's a long way from being a realistic approach for folks outside Dropbox, it seems.
It's a long way from being a realistic approach for anyone, including Dropbox, really.
What would you say are the biggest blockers to becoming realistic? I saw on the README they need tools in the Python ecosystem to start utilizing them, which I can help with starting with isort, beyond that I'd want to do whatever I can to help the project succeed.
Probably the biggest issue is that it can't run many libraries and frameworks because they use a lot of dynamic features, i.e. reflection and metaprogramming.

To be more specific: getattr, operator overloading, descriptors, heterogeneous dicts, decorators, etc.

Type checking and metaprogramming are fundamentally at odds [1]. Dynamic languages like Python have more of a focus on the latter. They later added type checking, but it comes at the "cost" of ruling out the more idiomatic metaprogramming and reflection features. In other words, static typing makes your source code bigger.

Well, optional typing to some degree lets you have the best of both worlds -- you can skip type checking of the hard parts. But optional typing doesn't let you compile your program to make it faster -- you need a fully-typed program for that.

----

I'm doing something similar to mypyc with https://www.oilshell.org/ (I actually visited Dropbox and chatted with them about it back in the spring.)

The difference is that I'm compiling Oil's Python source to C++ rather than to Python-C extension modules. So it doesn't depend on the Python runtime. It's not done but it's working well so far, and it's given me a lot of appreciation for which dynamic features Python programs actually use! (both my own and others)

Also note that mypyc was used to speed up mypy, which is a type checker. A type checker is a very particular kind of program that's different than 99% of the use cases of Python. So success on speeding it up is super impressive but it's not clear it generalizes.

The same is true for Oil -- my translation work doesn't generalize to arbitrary Python programs. Lots of people have died on that hill because it's really hard. You have a hard tradeoff between the kinds of Python programs you can support and the speedup you can give them. There are 10-20 projects over the last 2 decades at various points along that spectrum. In addition to mypyc, Oil's strategy was also inspired by Shed Skin, which is an impressive but mostly dormant Python-to-C++ compiler.

----

So in short I would say the problem is that nobody will be able to agree on a subset. You will have a lot of different fragments of Python geared toward particular use cases.

But Python will very often be more appealing than any of those fragments because it has a bigger ecosystem. One thing that I've appreciated more and more while designing a language is how much the network effects and inertia matter. It's why we're still using C and C++ after almost 50 years. I'm sure every day there is still a lot more C++ written than Go, Rust, Swift, and D combined, etc.

Python has a similar network effect and it will be around basically forever in its current form. Software doesn't really get rewritten or reduced -- more stuff just gets added on top.

[1] I wrote some posts about that tradeoff here: http://www.oilshell.org/blog/tags.html?tag=metaprogramming#m...

> type checking and metaprogramming are fundamentally at odds [1]

I would say that dynamic metaprogramming is at odd with type checking (and optimizations, and in general understanding the behaviour of a program statically). But of course metaprogramming can be done perfectly fine in a statically typed language.

I like Python, but I often wonder how many developers use Python because they actually use dynamic language features versus just liking the languages' clean syntax and library ecosystem. I'm surprised languages that offer both REPL (for development) and AOT native compilation (for production), like OCaml, are not more popular. Evidence that syntax matters, I guess. :)

mypy and mypyc are interesting but their compile-time checks and optimizations are still hampered by Python's dynamic language semantics.

Don’t underestimate inertia. I’ve worked with Python and Django for seven years. I know the libraries in the ecosystem. I know the framework. It’s far easier for me to start a project with Django than to learn another framework or language.
Names matter and OCaml is a crappy name.
If you think OCaml is bad, it used to be Caml Special Light, a play on Camel cigarette naming.
Christ..
The dynamic language semantics of Lisp, Scheme, Smalltalk, JavaScript have not hampered the existence of good JIT/AOT compilers.

Smalltalk, for example you can completely change the structure of a class by sending a become: message.

What I think is missing is a bit of more PyPy love, and the Truffle and OpenJ9 Python support efforts.

I think a great deal of this sort of thing could be done by just doing some eval in a dynamic state before you stop the vm and compile its stable state, rather than the actual source code.
I think you missed part of the point of what the article was trying to say - or rather, what they hoped to do with this strict python. One of those things being some form of hot code loading. A snapshot of the state can't be incrementally rebuilt - it's very much all or nothing; whereas if we know or modules are side effect free, or at least some useful part of module loading is, we could cache that part and get faster start up times on incremental changes.
Sounds like they need Erlang.
Python has some of my favourite syntax as well but I absolutely hate its annotations. TypeScript got typings right.

I think the killer language will be typescript with access to both the python and JavaScript ecosystems. We'll see what that looks like.

And of course if something changes the syntax, better anonymous functions will be the absolute first thing I would look for...

> TypeScript got typings right.

I have not used TypeScript, but looking at it's documentation the syntax for type annotations look identical. Would you be willing to expand on why you think its approach is better / how it's different?

No importing basic types, using binary operators instead of awful things like Union and bracket accessors and what not, inline interfaces...

Try it a bit. it truly is enjoyable. Fifteen years of python and I'm still enjoying TypeScript more.

Mypy is limited by annotations having to be compatible python syntax.

> using binary operators instead of awful things like Union

Do you mean like https://www.python.org/dev/peps/pep-0604/#id17 ?

> Inspired by Scala language [5], this proposal adds operator __or__() in the root type. With this new operator, it is possible to write int | str in place of Union[int,str]

Yeah. Good to see that's being added.
> No importing basic type

This at least is being solved in Python: https://www.python.org/dev/peps/pep-0563/

> inline interfaces

Typed dicts in 3.8 look pretty similar at cursory glance https://www.python.org/dev/peps/pep-0589/

I'll definitely play around with it more!

Watch the difference: A variable that can be an object with two elements (one of them being a list of strings), or a tuple of exactly two strings.

    // typescript
    let t: {a: string[], b: number} | [string, string]

    # python 3.8
    from typing import TypedDict, Tuple, Union
    class SomeTypedDict(TypedDict):
        a: List[str]
        b: Union[float, int]

    t: Union[SomeTypedDict, Tuple[str, str]]

I had to google a bunch to figure out how to write the Python version, whereas the typescript one was completely natural to write. It takes one line and requires no imports. The interface is inlined. All of this also makes it more readable when you come across it eg. in an IDE tooltip.
Granted, in python, I'd call the use of a typed dict a smell. If you're able to spend the time creating the typed dict, just promote it to a dataclass. Using python ~3.9, this will look like

    @dataclasses.dataclass  # or @attr.s
    class MyStruct:
        a: List[str]
        b: int|float

    t: MyStruct|Tuple[str,str]
But MyStruct will be an actual object that can be manipulated as an object. And if you want to accept any object that fits that interface, instead of just instances of MyStruct,

    class MyStructTmpl(Protocol):
        a: List[str]
        b: int|float
Then

    def f(thing: MyStructTmpl|Tuple[str,str]) -> bool:
        return True

    f(MyStruct(a=['a'], b=2))
would typecheck.

In JS having the typed-dict type makes sense because you're often working with arbitrary objects with who knows what attributes, but in python that isn't the case. There's fairly succinct and powerful tools (now, anyway) to define record types.

> I think the killer language will be typescript with access to both the python and JavaScript ecosystems. We'll see what that looks like.

I think this is an extremely good idea. Python is horrible but forced on a huge number of developers because of its ecosystem ... I think a bridging layer from typescript to python could be built in a way similar to swift’s Python Interop — and I don’t think it would require any special language support ...

I think could actually make a better/easier to use/more robust design than Swift by requiring all interactions with the python interpreter from node be async.

> Python is horrible but forced on a huge number of developers because of its ecosystem

This is a really interesting perspective to me. Coming from Python circles, I've heard too often how horrible JavaScript is as a language and how it's only used because the web has dictated it. Doing web development, I've used both, and generally am inclined to agree. I know TypeScript add some niceties on top of it, but it still is stuck with JavaScript baggage. My perspective has always been that Python is by far the better language, which is why people have written that eco-system in it despite the fact it doesn't have a built-in monopoly of the browser.

I don't think there is any sort of long-term future in anything "Python". I think a successful modern language has to have the potential for efficient concurrency baked in, which isn't really possible without breaking compatibility, and the Python community would never survive another round like the 2->3 transition. (And I'm not convinced the community really survived that one either, given the amount of ongoing bitterness about the whole situation).
Err, did you notice that Python is in the top 3 language in TIOBE (up from 4), and named "Language of the year"?

What bizarro bubble do you live in?

Python is very healthy at the moment, and still growing! However, I think that makes it even more important as a community that we don't rest on our laurels and we fix the issues we do have. CPU concurrency is definitely one of those issues.
> CPU concurrency is definitely one of those issues.

Yes, and each new version actually adresses that, one step at a time. (see https://bugs.python.org/issue35813 for example).

Indicators like TIOBE are VERY lagging. Like 5-10 years
In my assessment, not really. They are surprisingly spot on (for the top spots and singling out contenders).

Not to mention the language landscape is almost static at the top. Nobody's gonna came and take Python, Java, C, C++, JS, out in the next 10-15 years...

Only a huge self-blunder, like the Perl 5 -> 6 transition, and only at much more volatile time (when paradigms change, e.g. when web dev changed from CGI, Perl 5 had already lost the web framework scene to PHP, Rails, Django and the like even before losing its main niche back then - admin work) can do any serious damage to a top language...

Anyway, let's check in 5 years...

Well, for instance, TIOBE has Rust at #34, below such mainstays as ABAP, COBOL, and Scratch. It has Groovy above Ruby.
> I think a successful modern language has to have the potential for efficient concurrency baked in

I agree with this!

> I don't think there is any sort of long-term future in anything "Python".

I disagree with this :)

I think Python has efficient IO concurrency built-in already with async, and I feel it is likely that it finds a way to work out CPU bound concurrency long-term, as projects like sub-shells with channel communication demonstrate.

Efficient CPU concurrency needs either fine-grained locking (and removal of GIL), or data immutability.

Both seem rather hard to implement.

I predict that for CPU-intensive tasks, you'll keep using extensions in native code (like numpy or pytorch), or keep passing serialized objects through queues in multiprocessing setups.

https://github.com/jreese/aiomultiprocess The above is suffecient at FB scale for fairly intensive processing. Python is also sufficient for running quite a good bit of Instagram. I know some startups like to deploy over-engineered solutions but in reality Python is sufficient in many use-cases. You can always drop down to Cython if you have some hot path you need to optimize. (or Rust).
Data science/AI people breathed a whole lot of life into Python. I'm curious where it would be if they had went elsewhere.
R for the ecosystem, Julia for the performance & future proofing.
What "future proofing"? It's not like Julia is "the future" it's just a contended, and it's not doing that good at that either...
Its adoption at banks and life science research labs tells another story.

Naturally if the community finally gathers around PyPy, that might change.

Aside from having a two decade history of using C#, types is the only thing preventing us from going full Python. Even so, the dynamic types in Python are more often a benefit than a disadvantage because Python is so great at handling them automatic.

We build our employee database, and from there our IDM, from a singel XML file in a really shitty format + three txt files in even worse formats (they are single line output files from an old mainframe system predating sap). We used to do it in a rather complicated Microsoft SSIS workflow with a lot of C# services. All in all it’s a 30 minute nightly runtime. I recently replaced it with around 500 lines of Python and a 1-5 minute Runtime (sometimes at the beginning of a school year we’ll see changes to around 1000 positions).

Python eats the XML like it wasn’t shit. It takes things like terrible date formats, we’re talking the output of a SAP free-text box shitty, and ports then seamlessly into a SQL date field. This alone was a nightmare in C# and Python just does it.

Still, after two decades of strict types it feels dangerous.

I can imagine the next big programming language will be one that is split into two language-variants: the "low-level-variant" and the "high-level-variant".

The high-level-variant is a dynamic language with optional typing, which is good for scripting, fast prototyping, fast time-to-market, etc.

The low-level-variant is similar to the high-level-variant (same syntax, same features mostly, same documentation), but it has no garbage collector, typing is mandatory and it runs fast like C/C++/Rust. Compiled packages that are written in the low-level-variant can be used from the high-level-variant without additional effort at all. The tooling to achieve this comes with the language.

A language like this would be insane, IMHO.

A key consideration here would probably be the expression and passing around of managed instances spawned in the high-level variant through low-level code. Would you explicitly retain and release them? -- etc. I think it should be an ergonomic solution for this language to provide an edge over just using C / C++ / etc. with Lua / Python / etc.
You can say that this is typescript and assemblyscript, they have the same syntax but one of them is compiled natively (wasm).
Something in that direction, but I'd imagine it more like a Rust with a high-level-variant than a JS/TS with a low-level-variant ;)
For the sites I typically work on it’s very hard to give up the Django admin and all of the features it provides.

At the same time, I’d love a stronger type system to avoid a bunch of the pitfalls that the dynamism of python has.

So count me in.

Very much this! For numerical computing, Numba + llvmlite attempts to do it.

I don't know however if this approach could be extended to other domains - say making a web framework. Given, python classes let you do so much tinkering, any attempts to port existing code will probably need a lot of rewriting?

I'm hoping someone with more experience will chime in, but what about rpython / pypy?
How about Nim? It has Python-like syntax and is as fast as C. https://nim-lang.org/
Answered this below:

> I've been tracking nim, and would agree it's the most promising so far! I feel though that it's trying to be too flexible in many ways. Examples of this include allowing multiple different garbage collectors and encouraging heavy ast manipulation. I'm also afraid it is different enough to keep it from attracting a significant amount of developers from the Python community. Nonetheless, it's something I plan on using and contributing to, since it's the best option so far.

Though, now that another commenter pointed out mypyc: https://github.com/mypyc/mypyc I believe I'll invest my limited free-time in that project instead, as it will allow me to stay within the Python community and eco-system that I love so much.

Just in case it's of interest to anyone reading this, I interviewed the designer of Nim, Andreas, about his design choices and what he learned from Python and the C family here: https://sourcesort.com/interview/andreas-rumpf-on-creating-a...

Gives some good insight into where Nim is going in the future too.

It's certainly interesting to use! However, it's type checker still have a lot of work to go, since you can easily segfault due to using a nil reference.
There is https://github.com/python/mypy/tree/master/mypyc that I think is a great idea and approach
I completely agree. With python I need ten packages. With the shit show of JavaScript I need 100 conflicting packages. Why bother on a backend framework like js. it's a worthless language for backend development
Yeah I was hoping Nim would be it but I don't like the syntax they use.
Cython? Nuitka?
I use Cython a lot! But mostly to speed up existing Python code, and build C-extensions faster. I don't see it as a strict subset of Python or a new language to build a community around. Nuitka I just started experimenting with to build standalone Python executable, and I really like the direction and roadmap they are following. In the end though both of these technologies seem like ways to somewhat speedup existing Python code and not attempts to introduce a strict language subset that would allow the greatest amount of optimization, and finally fix long running issues, like the inability to have multiple versions of a package installed.
As far as I understand it, RPython isnt' really meant for actually writing programs in:

> Do I have to rewrite my programs in RPython?

> No, and you shouldn’t try. First and foremost, RPython is a language designed for writing interpreters. It is a restricted subset of Python. If your program is not an interpreter but tries to do “real things”, like use any part of the standard Python library or any 3rd-party library, then it is not RPython to start with. You should only look at RPython if you try to write your own interpreter.

See: https://rpython.readthedocs.io/en/latest/faq.html

Hmm, still it might make a good foundation for the sort of thing you're talking about, eh?

Cheers.

For sure!
nim
I've been tracking nim, and would agree it's the most promising so far! I feel though that it's trying to be too flexible in many ways. Examples of this include allowing multiple different garbage collectors and encouraging heavy ast manipulation. I'm also afraid it is different enough to keep it from attracting a significant amount of developers from the Python community. Nonetheless, it's something I plan on using and contributing to, since it's the best option so far.
> allowing multiple different garbage collectors

How's that a problem?

Sounds like Go. ;) This is a cheeky remark, but I use Python and Go, and Go very much feels like an improved Python in most ways. Especially when it comes to static analysis, build tooling, distribution, performance, etc. In particular, I love that there are no venvs, pipenvs, virtualenvs, pyenvs, wheels, eggs, setuptools, easy_installs, etc.
What Go adds in tooling and performance, it takes away in expressivity.

What takes 3 lines in Python, takes 10-30 on Go.

Yeah, never understood what a Python Dev could find attractive in Go, besides not having to deal with C instead.

Which is positive mind you, but they would be better served by adopting PyPy.

Pypy is super cool, but it doesn’t solve for maintainability and it only improves performance by one order of magnitude, leaving it 1-2 orders slower than Go. Besides, IMO, goroutines are so much nicer than Python’s async.
Yeah, I totally agree. I love Python, but I can't stand Python's async situation. I don't like Go, but I think its concurrency is miles better.
Yeah, but typically only locally. Like using a for loop instead of a list comprehension, or handling errors. So more keystrokes, but in most cases not more complexity. In some cases (generic programming), Python really is more expressive, but those ~5% of cases aren’t worth the tooling/perf/maintainability tradeoffs most of the time.
It depends.

Code reviewers glazing over copy-pasted boilerplate blocks can more easily lose track of the whole, and miss an error which is obvious when the whole is expressed in 10 lines.

There is some optimal range of expressive density for comfortable use by humans. APL or K likely above that level, and Go feels below it, not as low as COBOL, but still.

The opposite is true in my experience. Most of that boilerplate is brackets and indentation, which visually frame the interesting bits, drawing your eye to them. This is, of course, subjective, but I use both regularly and at worst this is not a problem for Go.
The problem here is that there is boilerplate at all. There shouldn't be.

Boilerplate distracts from what is actually going on. I can generally identify code smells from the shape of python code (like, blur all the text so I can't read the words, and the shape of the blocks tells me everything I need), I can't do the same in go, because there's so much more indentation and visual stuff happening, and most of it (boilerplate error handling) isn't interesting.

I'm curious what you see as the tooling benefits of go?
Build tooling (“go build” vs setup.py), type checker, text editor support (hovering over a symbol for the type and docstring), documentation generator / godoc.org, dependency management (pip is great but it’s not reproducible; go’s toolchain is only modestly better here IMO), no need for virtualenvs, etc. I’m sure I’m missing several.
> build tooling

All my projects now use poetry for the full build tooling and I love it. No setup.py needed just include any settings in the standard pyproject.toml file example: https://github.com/timothycrosley/portray/blob/master/pyproj..., which can be generated with poetry's help using poetry init.

> text-editor support

I feel like Python with type hints (for all their current flaws) does give you this exactly.

> dependency management

Again I think poetry solves the problems here very nicely

> documentation generator

Personally, I like portray better than anything in the Golang world for this https://timothycrosley.github.io/portray/ I may be biased since I wrote it.

I write a lot of Python tools so I'm genuinely curious because if there were unfilled needs I would want to address them as one of my 52 projects: https://timothycrosley.com/

I hate the fact that you may be right, because I really don't like Go in many ways:

- I hate it's module system and package eco-system story. - I don't like its syntax. - I don't like its error handling. - I'd much prefer gradual typing. - I want to maintain the ability to use interactive interpreters. - I don't like the fact that instead of being community driven it is Google driven.

But, anecdotally, I see go being used as a second language to Python more than anything else and at an ever accelerating rate.

These are all fair points. I really enjoy Python, but there are too many things I fight with on a regular basis that simply aren’t issues in Go. It could be so much better if (1) there was a better type system (mypy is unnecessarily shoehorned into the syntax and still very broken—can’t even express recursive types like JSON), (2) a good way to constrain the dynamism so performance could be improved, and (3) a better environment/package management and distribution story (so far pantsbuild.org and PEX files are the best I’ve found). Then there are a long tail of more minor issues, like async/await vs goroutines, real parallelism, etc.
> type-system

I agree, but if you for instance look at the TypeScript comparison sub-thread, you'll see that all the issues with both the syntax and implementation of the type-system are being aggressively resolved, and likely will all be so by 3.9.

> Good way to constrain the dynamism so performance could be improved

Couldn't agree more!

> environment

I find poetry a joy to use. If you want to bypass venvs all together, there's a lot of work to make that a reality, such as https://github.com/David-OConnor/pyflow.

> packaging

Python in 3.5 added complete zip app support, which has improved this dramatically from my perspective. Extended by things like shiv https://github.com/linkedin/shiv make it fairly complete.

> async/await

This is interesting to me. I prefer async/await in general, because it has become a standard across programming languages and I find it really easy to reason about. I also find channels to be too widely seen as a cure-all, when the only study so far has shown they actually led to an increase bug count. But I don't discount the value of real-parallelism, and am glad to see that Python has been pushing harder on that lately, with things like subshells that allow bypassing the GIL on a single thread.

> I agree, but if you for instance look at the TypeScript comparison sub-thread, you'll see that all the issues with both the syntax and implementation of the type-system are being aggressively resolved, and likely will all be so by 3.9.

I'm happy to hear that; hopefully the efforts really do address these issues well.

> I find poetry a joy to use. If you want to bypass venvs all together, there's a lot of work to make that a reality, such as https://github.com/David-OConnor/pyflow.

I'll have to check those out, but one inherent problem is that even if these tools really do solve my pain points, adopting them means I'm leaving my org on a relatively small island, isolated from the Python community. If these really are the holy grail, why isn't the broader Python community adopting them? Please don't take this as me looking for something wrong--whatever Python build tool I use, I'll eventually need support and there's a lot to be said for having a thriving community that has almost always run into my exact problem before.

> Python in 3.5 added complete zip app support, which has improved this dramatically from my perspective. Extended by things like shiv https://github.com/linkedin/shiv make it fairly complete.

We're currently using this via pex. It mostly works, but we still run into problems occassionally (system dependencies, for example). Figuring out how to integrate these tools into the broader build process is another problem to solve--we're using `pants` which supports pex out of the box, but we're running into lots of bugs or other problems. I'll keep an eye on shiv.

> This is interesting to me. I prefer async/await in general, because it has become a standard across programming languages and I find it really easy to reason about. I also find channels to be too widely seen as a cure-all, when the only study so far has shown they actually led to an increase bug count. But I don't discount the value of real-parallelism, and am glad to see that Python has been pushing harder on that lately, with things like subshells that allow bypassing the GIL on a single thread.

My biggest issues with async/await are

(1) every package needs an async variant (async boto, async docker, etc etc). We work around this by running them in a thread pool executor, and I think that works, but I don't know if I'm holding the GIL unnecessarily and causing performance issues (fundamentally difficult to diagnose). This is roughly the "what color is my function" problem.

(2) it's really easy to starve the event loop by calling into something that transitively makes a sync call or otherwise just does a lot of CPU-heavy work. We've run into both kinds in production and they've been really hard to troubleshoot (because the requests that time out often aren't the ones that are actually causing the problems).

(3) dynamic typing means it's super easy to forget to await things. Tests should catch this, but we find ourselves writing tests _just_ to catch this (e.g., we now write tests for entrypoints that _just_ `await lib_function(params)`; we would normally not write tests for such simple functions, but now we have to). Static typing is the right way to solve this and mypy does, but mypy has too many other issues (at the moment) for our org.

One substantial criticism of goroutines is that they're less safe than async/await because you need to make sure the code you're running is threadsafe. I appreciate this criticism, but I think it's the right tradeoff for Go's performance aspirations (another great high-performance alternative is Rust's borrow checker, but that's the wrong tradeoff for Go's developer productivity aspirations).

> I'll have to check those out, but one inherent problem is that even if these tools really do solve my pain points, adopting them means I'm leaving my org on a relatively small island, isolated from the Python community. If these really are the holy grail, why isn't the broader Python community adopting them? Please don't take this as me looking for something wrong--whatever Python build tool I use, I'll eventually need support and there's a lot to be said for having a thriving community that has almost always run into my exact problem before.

Only because they are so new. portray was built a few weeks ago and already has a thriving community building around it - but of course it's still a small drop of the whole ecosystem. Older tools I've built like isort, are now ingrained into the community: https://github.com/timothycrosley/isort, but that took years, even without major issues or complaints being present. It just takes people time to adopt new things.

Go may "feel" like Python, but it's almost nothing like Python in actual practice. It's not dynamic (and doesn't even have generics), and its error handling is dramatically different.
It _is_ like Python in practice (I use both languages all the time). That’s largely why you see it used in many of the same places as Python. It has dynamic features by way of interface{}, which is every bit as “generic” as what Python has to offer. :) But yes, the error handling is different—values vs exceptions.
I am of the view that interface{} is the worst of both worlds with regards to static/dynamic typing. Dynamically typed languages typically have type coercion and structures that make dealing with vars with unknown types easy.

However golang doesn't have that. So you get the danger of a dynamic language without the features that make powerful.

Go has those features in the reflect package (so as far as I know, Go is just as powerful as Python), but you’re right that they aren’t easy to use. If you do use them, it’s quite clear, and will be addressed in code review so you don’t have nearly as many dynamic typing bugs as Python—it’s not anywhere close.

Very dynamic code shouldn’t be easy; the happy path should encourage clear, simple code. By encouraging people to stay on the happy path, their code is more performant, maintainable, etc and it keeps the average code quality quite high across the ecosystem.

It is rare that I have had problems with dynamic typing errors in P* languages. I am of the view that that dynamic code should be one of two things.

1) Super easy. That way doing it right is trivial. 2) Impossiblely difficult so the only people who are doing it can be trusted to do it right.

To me go falls between those two. It’s real easy to say interface{} (indeed it is more difficult to make a non empty interface) but doing it in a way that is safe isn’t easy.

I don’t think expressive power is the point here. As they are both compleate languages. More it is an issue of what trade offs and comprises have been made.

Go has generic types the same way Python has macros, or the same way C++ templates is a functional programming language.

C has void *, writing generic code using it is hell. Enough so that people went through a lot of trouble creating C++ and later Rust to escape it.

I'd say the type casting from interface{} to whatever you assume is in there qualifies as different.

Pretty much every single aspect of these languages is different from what I can see, the only thing they have in common is included batteries, the rest is growing popularity and consequences thereof.

Yeah, I get it. It’s a little disingenuous of me to say that interface{} qualifies as generics, but I can’t quite put my finger on why it is different than Python. Neither are typesafe (although mypy supports generics, but has many other issues), but in any case typesafe generics would I think improve Go.