Hacker News new | ask | show | jobs
by jennyyang 1956 days ago
Anything that breaks and changes semantics should not be allowed into the language. Let Python be Python, not a Frankenstein's monster of ideas like C++. If it were an idea that were Pythonic, you would not see the confusing examples I've seen in the comments. C++ is the poster child of trying to do too much with the language and it losing itself due to death-by-committee. It's very sad that Python has started down this road.
4 comments

We seem to be seeing a paradigm clash. On one side, there are people who are concerned with whether or not a feature is desirable. On the other side, there are people who are concerned with whether or not a feature is desirable and also Pythonic.
> On one side, there are people who are concerned with whether or not a feature is desirable.

The thing is this: You can add something which, in isolation, seems desirable and positive -- but in the greater picture, is a net negative due to the complexity it adds.

People might say that those who do not like the pattern matching syntax are not obliged to use it. But when developing code in longer-running projects, far more code is read than written. Adding syntax, especially with complex edge cases, especially from languages which use concepts that are at the core quite alien to Pythons main concepts, adds a burden which is difficult to justify.

Very much so. I run into this with Clojure. It has so many different ways to skin every cat, each with its own unique blend of quirks, that it can be quite difficult to understand other people's code, and, by extension, use the language in a team setting.

That sort of experience leaves me thinking that this is a very dangerous turn to take for a language whose core ecological niche is, "Easy for professionals who don't have a BS in CS to understand and use productively." Lines 2 and 13 of PEP 20 are core to why Python, of all languages, came to dominate that niche. I am beginning to fear that the Python core team, being composed primarily of software engineers, is ill-equipped to properly understand that.

Yap. It doesn't make sense to destroy the language just to get in a particular feature. You don't need a language to do everything. It needs to be good at everything it's meant to be good at.
I very much want to agree with you. Only that I do not know any more what "Pythonic" is supposed to mean.

One thing that Larry Hastings refers to seems often to be underestimated - readability.

It seems nice to be able to do code golfing and use pattern matching to reduce an expression from maybe 50 lines to 10.

But what matters far more is that one can read code easily, without guessing, and without consulting definitions of edge cases. Even in smaller projects, one will read 10 times more code than one writes. In larger projects and as a senior programmer, that could be a factor 100 or 1000. Not that unusual to work one week through a bug in someone else's code, and fix it by changing a single line. As code becomes more complex, it becomes really important to understand exactly what it means, without guessing. This is key for writing robust, reliable and correct code (and this is perhaps why the ML and functional languages, which stress correctness, tend to be quite compact).

And while it might be satisfying puzzle-solving for smart and easily bored people, like you and me, to write that pattern matching code and reduce its length, it is just not feasible to read through all the PEPs describing surprising syntactic edge cases in a larger code base.

I can only agree. Compared to other languages I find Python increasingly difficult to reason about, mainly due to its dynamicity. If the language complexity increases as well from now on I don't think I will use Python unless absolutely necessary.

Meanwhile, Julia supports pattern matching due to macros: https://github.com/kmsquire/Match.jl

Unfortunately Match.jl has gone unmaintained for a while, but we do have MLStyle.jl

https://github.com/thautwarm/MLStyle.jl

https://thautwarm.github.io/MLStyle.jl/latest/syntax/pattern...

That looks great, thanks.
Some of the english documentation can be quite hard to read, but the code is very useful. Submitting PRs to improve the documentation is welcomed by maintainers.
Pattern matching is the brainchild of ML. Python, being a multi-paradigm language with the strong functional side, missed this simple in concept and powerful in practice language concept.
> Python, being a multi-paradigm language with the strong functional side

Coming back to that, just a reminder that lambdas in Python are still gimped, closures do not work as expected because of -- scoping, and core developers in Python 3 tried to remove with "map" and "filter" tools that are considered quite essential for functional programming.

I actually wish they had done so.

As someone who switches between Python and functional languages, I find Python's "map" and "filter" to be a trap, and have taken to scrupulously avoiding them. The problem is that I expect those functions to be pure, and, in Python, they aren't. They actually can't be, not even in principle, because their domain and range include a core datatype that cannot be interacted with in a pure manner: generators. A generator will change its own state every time you touch it. For example:

  >>> seq = (x for x in range(1, 11))
  >>> list(filter(lambda x: x % 2 == 0, seq))
  [2, 4, 6, 8, 10]
  >>> list(filter(lambda x: x % 2 == 1, seq))
  []
In a language that is a good fit for functional programming, the last statement would return [1, 3, 5, 7, 9], not an empty list. But Python is imperative to the core, so much so that I would argue that trying to use it as a functional language is like trying to drive screws with. . . not even a hammer. A staple gun, maybe?

(Which isn't to say that you can't successfully use some functional techniques in Python. But it's best done in a measured, pragmatic way.)

A good example why immutability by default seems to be the right thing - in Clojure, "seq" would not have been modified by the first filter expression:

    user=> (def seq_ (range 1 11))
    user=> (filter (fn [x] (== (mod x 2) 0)) seq_)
    (2 4 6 8 10)
    user=> (filter (fn [x] (== (mod x 1) 0)) seq_)
    (1 2 3 4 5 6 7 8 9 10)
or more concisely:

    user=> (filter even? seq_)
    (2 4 6 8 10)
    user=> (filter odd? seq_)
    (1 3 5 7 9)

And also an example why it does not work to go and grab one or another desirable feature from a functional language, they need to work together.

> (Which isn't to say that you can't successfully use some functional techniques in Python. But it's best done in a measured, pragmatic way.)

A great example how it is done right is Python's numpy package. The people who created that knew about functional languages and APL (which fits nicely in since Python's predecessor ABC had some APL smell). The obviously knew what they were doing, and created a highly usable combination of a general data type and powerful operations on it.

I found this very surprising so asked a Python-knowledgeable acquaintance who mentioned that this works as expected

    >>> seq = [x for x in range(1,11)]
    >>> list(filter(lambda x: x % 2 == 0, seq))
    [2, 4, 6, 8, 10]
    >>> list(filter(lambda x: x % 2 == 1, seq))
    [1, 3, 5, 7, 9]
Right. Because it's doing two different things. One is working with lists, the other is working with lazy calculations.

A common functional pattern is to do lazy calculations, so that you don't have to store every result in memory all at once. The subtext I'm getting at is that a language that has footguns that make it dangerous (for reasons of correctness, if not performance) to reuse and compose lazy calculations is a language that is fundamentally ill-suited to actual functional programming.

Which is fine! Python's already a great procedural-OO language. It's arguably the best procedural-OO language. Which is a big part of why it's taken over data science, business intelligence, and operations. Those are, incidentally, the domains where I feel just about the least need to program in a functional paradigm. And, in the domains where I do have a strong preference for FP, Python is already a poor choice for plenty of other reasons. (For example, the global interpreter lock eliminates one of the key practical benefits.) No amount of risky, scarring cosmetic surgery is going to change that.

OK I see, the very short sequence in your example was a stand-in for a potentially infinite one. I got nerd-sniped into understanding what was happening as I found the behavior surprising.
To be fair, that's a foot gun in haskell as well. Using lists non-linearly like this in haskell gives you the correct results but at a 10x performance tax or worse because it can't optimize into a loop anymore.
At least to me, a footgun goes beyond a mere performance gotcha. There's a whole category difference between, "If you do this, the compiler may not be able to optimize your code as well," and, "If you do this, your code may produce incorrect results."
> Python, being a multi-paradigm language with the strong functional side

I would doubt that. Surely, things like Numpy are written in a functional fashion, but Python relies very much on statements, iteration, things not being an expression, almost every named symbol except string and number literals being mutable, and there are no block-scoped name bindings which are essential to functional languages.

And the attempt to add the latter to Python might end in a far bigger train wreck than C++ is already.

Mixing OOP and functional style works, more or less, for Scala, but everyone agrees that Scala is a hugely complex language. And in difference to Python, it has immutable values.

What could turn out better would be to create a new language which runs interoperable in the same VM (much like Clojure runs alongside Java and can call into it). And that new language, perhaps with the file extension ".pfpy", would be almost purely functional, perhaps like a Scheme or Hy, without these dreaded parentheses. That would at least leverage Python's existing libraries.