News for hackers. Stuff that matters.
Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @01:07AM (#20672596)
I'm not the person you're responding to but what they say resonates with me as a Python developer.

I believe Python is quite possibly the best language for a few things

* Exploratory programming - such as what data scientists do

* Writing programs that will never grow beyond 150LOC, or roughly what fits on a screen + one page down

When I have those two constraints met I am almost always choosing Python.

Here are some problems I face on codebases as they scale up:

* Python conventions lead to what I consider bad code. Devs will often choose things like 'patch' over dependency injection, and I have seen on multiple occasions projects derided for providing DI based interfaces - "oh, why would you write code that looks like Java? This is Python".

There's a lot of death by a thousand cuts. Keyword args in functions are abused a lot, because they're "easy", ability to patch at runtime means it's often "easy" to just write what looks like a simple interface, but it's then harder to test. Inheritance is "easy" and often leads to complex code where behaviors are very, very non-local.

Dynamic types mean that people are often a little too clever with their data. I've seen APIs that return effectively `Union[NoneType, Item, List[Item]]` for absolutely no semantic reason, without type annotations, meaning that if you assumed a list or none came back you were missing the single Item case. The implementation actually internally always got a list back from the underlying query but decided to special case the single item case... why? I see this sort of thing a bit, and other languages punish you for it (by forcing you to express the more complex type).

* I find myself more and more leveraging threads these days. I did this often with C++, and all the time in my Rust code. Python punishes you for threading. The GIL makes things feel atomic when they aren't, reduces you to concurrency, all while paying the cost of an OS thread. Threading primitives are also quite weak, imo, and multiprocessing is a dead end.

And, really, Python is anti-optimization, which is a good and bad thing, but it's a bit extreme.

* Implicit exceptions everywhere. I see 'raise Exception' defensively placed in every Python codebase because you never know when a piece of code will fail. I see a lot of reliance on 'retry decorators' that just catch everything because you never know if an error is transient or persistent.

The common "don't use exceptions for control flow" is broken right off the bat with StopIteration. I just think error handling in Python is god awful, really.

* Mypy is awesome, but feels like a bit of a hack. The type system is great, and in theory it would solve many problems, but coverage is quite awful in open source projects and the type errors I get are useless. I actually only use mypy to make my IDE behave, I don't run it myself, because my types are actually technically unsound and the errors are too painful to work with (and honestly the type system is quite complex and hard to work with, not to mention the bugs).

There are lots of other smaller issues, such as my distaste for pip, py2/3 breakage, lack of RAII, the insane reliance on C all over the place, I think syntax is bad in a lot of places (lambdas, whitespace in general), etc but these are probably my main sticking points with using the language for large projects.

Again, Python is actually my favorite language for specific tasks, but I really think it's best optimized for small codebases.

Re:Python Is Eating the World by geewee on Monday August 12, 2019 @01:12AM (#20672619)
The common "don't use exceptions for control flow" is broken right off the bat with StopIteration. I just think error handling in Python is god awful, really.

That's not an idiom python has ever subscribed to though, it's always subscribed to the "Better to ask forgiveness than permission" - I think there are strengths to both viewpoints, but honestly I think "don't use exceptions for control flow" is more of a convention that a "truth"

Re:Python Is Eating the World by ComputerGuru on Monday August 12, 2019 @02:11AM (#20672872)
> honestly I think "don't use exceptions for control flow" is more of a convention that a "truth"

In absolute terms or when coding on paper, perhaps. But in the real world and if performance even remotely matters, it’s as close to a universal rule all languages end up embracing or turning into creaking hulks of slow code given how exceptions work in practice at the level of cpu execution units.

C#/IL/.NET embraced excerpts heavily at around the same time (start of the 2000s) but in time developers (in and out of Microsoft) learned the hard way that it doesn’t scale. With .NET core, exceptions for flow control are completely verboten and APIs have been introduced to provide alternatives where missing. Exceptions should be so rare if you chart all handled exceptions, you shouldn’t see any and would thoroughly explore why one or more pop up when a system or dependency hasn’t exploded.

Re:Python Is Eating the World by goto11 on Monday August 12, 2019 @04:18AM (#20673518)
If you care that much about performance you probably shouldn't use Python in the first place. Python deliberately prioritize ease of development above performance.
Re:Python Is Eating the World by momentoftop on Monday August 12, 2019 @03:12AM (#20673156)
The Ocaml exception implementation is comparatively very performant, and so its exceptions are routinely used for control flow.
Re:Python Is Eating the World by 9q9 on Monday August 12, 2019 @05:36AM (#20673864)
I'd be interested to learn about how exceptions are typically using in Ocaml for non-local control flow.

Exceptions have two core properties: (1) non-local jump (carrying a value) and (2) dynamic binding of place to jump to. Contrast this with e.g. break / continue in loops where (2) does not hold. If most use cases of performant OCaml exceptions were not making use of (2) that would be an interesting insight for programming language design.

Have you got data on this matter?

Re:Python Is Eating the World by momentoftop on Monday August 12, 2019 @06:45AM (#20674207)
No data as such. But here's the first example in the "batteries included" list library:

   let modify_opt a f l =
     let rec aux p = function
       | [] ->
         (match f None with
          | None   -> raise Exit
          | Some v -> rev ((a,v)::p))
       | (a',b)::t when a' = a ->
         (match f (Some b) with
          | None    -> rev_append p t
          | Some b' -> rev_append ((a,b')::p) t)
       | p'::t ->
         aux (p'::p) t
     in
     try aux [] l with Exit -> l
Here, the Exit exception is being raised as an optimisation: if no modification happens, then we return the original list, saving an unnecessary reverse. The try is the only place that the Exit exception is caught, so the jump location is static, much like a break.
Re:Python Is Eating the World by 9q9 on Monday August 12, 2019 @07:32AM (#20674438)
Thanks.

That's, as you say, a statically scoped fast exit. One does not need the full power of exceptions for this (exceptions'd dynamic binding comes with a cost). If exceptions are widely used for this purpose, one might consider adding a nicely structured goto to the language. Something like linear delimited continuations?

Re:Python Is Eating the World by momentoftop on Monday August 12, 2019 @08:42AM (#20674855)
Sorry, I misunderstood what you were after. Ocaml exceptions are used more generally, and often make use of 2.

For instance, the basic stream/iterator in Ocaml is Enum, which always uses an exception to signal when the stream has been exhausted, rather than providing a "has_next" predicate.

The catch site of this exception is dynamic.

Re:Python Is Eating the World by 9q9 on Monday August 12, 2019 @08:53AM (#20674961)
That is interesting. Once could argue that since fast exceptions contain non-local jumps with a static target, it is enough to supply only the former, so as to simplify the language.
Re:Python Is Eating the World by Rapzid on Monday August 12, 2019 @04:28AM (#20673557)
Interestingly .Net has very performant exception handling as well. Not sure if something changed internally with Core, but using exceptions in place of return error codes was incredibly common within the .Net ecosystem.
Re:Python Is Eating the World by profquail on Monday August 12, 2019 @07:02AM (#20674306)
.NET has always had slow exception handling because it tied/ties in Windows’ Structured Exception Handling (SEH); that’s rather slow but provides e.g. detailed stack traces even for mixed-mode callstacks.

Having ported some decently large codebases from OCaml to F#, the heavy use of exceptions in OCaml (where exceptions are very lightweight by design) had to be changed to normal control flow with monads to achieve good performance in F# — specifically because of .NET’s slow exception handling.

Re:Python Is Eating the World by hdfbdtbcdg on Monday August 12, 2019 @02:03AM (#20672831)
> The common "don't use exceptions for control flow" is broken right off the bat with StopIteration. I just think error handling in Python is god awful, really.

Python explicitly does not agree with this...

The same for a lot of your other complaints. It sounds like you are trying to write some other language in Python. Similarly if someone in a team using Java tried writing Python in Java they would complain a lot and end up with ugly hard to work with Java.

Re:Python Is Eating the World by Animats on Monday August 12, 2019 @02:58AM (#20673095)
Python's exception system isn't bad. It's way ahead of Java's or C++ exceptions. The gyrations people go through in Go or Rust to handle errors are far worse. The exception hierarchy lets you capture an exception further up the tree to subsume all the more detailed exceptions. (Although the new exception hierarchy in 3.x is worse than the old one. In 2.x, "EnvironmentError" was the parent of all exceptions which represented problems outside the program, including the HTTP errors.[1] That seems to have been lost in 3.x).

I think StopIteration is being removed. That was a a bad idea.

Re:Python Is Eating the World by bakery2k on Monday August 12, 2019 @03:20AM (#20673207)
I haven't seen anything about `StopIteration` being removed?

In fact, the Python developers recently doubled-down on the design by introducing `StopAsyncIteration`.

Re:Python Is Eating the World by brylie on Monday August 12, 2019 @03:49AM (#20673368)
PEP 479 might be what GP was referring to: https://www.python.org/dev/peps/pep-0479/

PEP 479 is specifically related to generators:

> If a StopIteration is about to bubble out of a generator frame, it is replaced with RuntimeError, which causes the next() call (which invoked the generator) to fail, passing that exception out. From then on it's just like any old exception.

Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @02:04AM (#20672841)
> Python explicitly does not agree with this...

OK, I'm willing to contend that that is the case. It doesn't have much to do with my overall issue with error handling.

> The same for a lot of your other complaints. It sounds like you are trying to write some other language in Python. Similarly if someone in a team using Java tried writing Python in Java they would complain a lot and end up with ugly hard to work with Java.

This is a very loose criticism of my post, I don't know how to respond to this. I've written Python for years, I think I gave it due credit for what it's good at.

I am not trying to write some other language with Python, I just think Python is not a very good language compared to others given a lot of fairly typical constraints.

Re:Python Is Eating the World by brylie on Monday August 12, 2019 @03:51AM (#20673383)
Writing programs that will never grow beyond 150LOC, or roughly what fits on a screen + one page down

I've written Python for years, I think I gave it due credit for what it's good at.

I'm not sure how fair this assessment is, considering the multitude of Python projects of considerable size and scope.

Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @10:10AM (#20675546)
Why? Of course it's entirely possible to build programs of a larger size. I'm just saying that after a certain point you start hitting walls. There are tons of ways to hedge against this - leveraging mypy early on, spending more money (on eng, infra, ops, etc), architecting around the issues, diligence, etc.

It would be very silly for me to say that you can't build large systems in Python, I've worked on plenty that are much larger myself.

Saying a language is possibly the best for two important use cases (exploration, small programs) is quite a statement, in my opinion. I don't think I believe that there's a language that excels so well in, say, building web services.

Re:Python Is Eating the World by tayo42 on Monday August 12, 2019 @02:28AM (#20672942)
> Python explicitly does not agree with this...

Whats the convincing argument for defending this? Ive always heard its just the way python is. Through experience using exceptions for anything other then exceptional situations and errors seems messy.

Re:Python Is Eating the World by jimnotgym on Monday August 12, 2019 @03:04AM (#20673120)
I suppose that comes back to use cases. I am one of the 'getting things done crowd', rather than a computer scientist. A lot of my work had been in things like EDI, or similar integration code.

Imagine you are working through a series of EDI files and trying to post them to a badly documented Rest(ish) API of some enterprise system. If the file is bad (for whatever reason) you need to log the exception and put the file in a bad directory.

Pythons use of exceptions for control flow is perfect for this. If file doesn't load for whatever undocumented reason, revert back to log and handle the fallout.

"Oh I see a pattern, this API doesn't like address lines over 40 characters, I will add a custom exception to make the logging clearer, and go and try and see if I can fix this upstream. If not I will have to write some validation"

It is this dirty world of taking some other systems dirty data and posting it to some other systems dirty API that I find Python rules.

I have never worked on a large application where I owned the data end-to-end. Maybe there are better choices than Python for that?

Re:Python Is Eating the World by macleginn on Monday August 12, 2019 @03:40AM (#20673329)
My feelings exactly. I write a lot of kinda scientific code that is not really computational but takes one or more horribly messy datasets and combines them to do some analysis. I now routinely use exceptions to filter out bad/complex data-points to deal with later.
Re:Python Is Eating the World by danielscrubs on Monday August 12, 2019 @05:24AM (#20673788)
Data scientist here. It’s great because non programmers find it easy and we have so so many tools available.

That said, the amount of cpu cycles wasted even with best of breed pandas is insane, and when you want to do something “pythonic” on big data it all falls down. When you want to deploy that model you are also going to have problems.

That said, it’s still the best tool for the job, but it’s certainly not because of the creators of Python.

Re:Python Is Eating the World by lozenge on Monday August 12, 2019 @06:00AM (#20673999)
Can you explain the use case for your unsound type system? Is it unsound just because mypy types are inexpressive?
Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @10:14AM (#20675573)
My opinion on static type systems is that, unless you're actually doing a more type driven development style and leveraging the type system, the single greatest benefit is IDEs will autocomplete for you, give you 'jump to' etc.

Python without type annotations can be painful in an IDE - it chokes a lot on those features.

Since I don't take a very type driven approach to Python (it would be too slow since I'd have to figure out 3rd party library types and shit like that) I just write annotations in places where I personally know the type. Mypy complains about this for various reasons - probably because my annotations are not technically correct, because I'm not a pro at generics in mypy and working out inheritance, as well as general mypy issues like poor support for class methods and that sort of thing.

But I ignore all of those errors because the IDE can still work things out.

Re:Python Is Eating the World by konschubert on Monday August 12, 2019 @03:18AM (#20673192)
What you’re describing is what happens when you have a developer who’s bad at their job.

I see the point that python invites these anti patterns.

But on the other hand, a software developer that returns Union(list[item], item]) in Python is probably also going to mess up a Java program sooner or later.

Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @10:15AM (#20675584)
I don't think you'd ever write that in Java because Java would punish you for it. You can always blame the programmer, but I'm always going to blame the language for making 'bad' easy.
Re:Python Is Eating the World by feanaro on Monday August 12, 2019 @08:48AM (#20674907)
> and multiprocessing is a dead end.

Why is multiprocessing a dead end?

Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @10:15AM (#20675579)
Huge overhead for process communication, weird interface, weird semantics, etc.
Re:Python Is Eating the World by joshuamorton on Monday August 12, 2019 @12:58PM (#20677337)
Is that still true with processpoolexecutor?

The overhead obviously is still there, but the interface is a drop in replacement for threadpoolexecutor, which looks basically just like a multithreaded/async/future-based collect.

Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @02:11PM (#20678196)
The interface may have improved, I haven't paid attention to mp in a long while. If I'm reaching for mp I generally just accept that I'm using the wrong language.
Re:Python Is Eating the World by feanaro on Monday August 12, 2019 @02:22PM (#20678315)
Can you expand a bit on weird semantics and the overhead? Are you referring to some particular benchmark?
Re:Python Is Eating the World by staticassertion on Monday August 12, 2019 @09:24PM (#20681952)
I have a hard time reasoning about multiprocessing code, for various reasons. It's bitten me in a lot of weird, distinct ways, not really worth listing here.

To communicate across mp you use pickle, which is a pretty significant performance impact relative to something like threading. There's also the issue of copy on write memory + reference counting interacting poorly.

Re:Python Is Eating the World by feanaro on Tuesday August 13, 2019 @02:37PM (#20688897)
> To communicate across mp you use pickle

I suspect this is the root cause of the difference in our experiences. My uses of mp have usually been somewhat more "embarrassingly parallel", for instance having a list of data elements which need to be processed with the same algorithm. For this use case, the usage of mp is pretty simple, often only a `pool.map(f, xs)`.

I can imagine that pickle might have tricky edge cases and/or be slow.

Re:Python Is Eating the World by staticassertion on Wednesday August 14, 2019 @11:52AM (#20696804)
Sure, but:

a) All reference counts after the fork are going to cause a copy of memory, so any memory access (even a read) can trigger a copy.

b) Even to send 'xs' over you must serialize it via pickle, and then the callee must deserialize it

That may be fine for your use case but it's strictly worse than just sharing a reference across a thread.