Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I strongly think Python should have more functional programming support. Functional Programming Languages are usually scary to look at for many programmers. Python would not only be a good FP introduction to them, it would also benefit greatly.

Years ago I found out the Guido wouldn't let tail recursion included, and even tried to remove map function from built in functions. Therefore I got the impression that python wouldn't have further support in FP. I really wish that is not the case. With the coming pattern matching in 3.10, My hope is high again.

I have very high respect for Guido van Rossum, I'm not here to discredit him. He's one of the authors in PEP 634.

I wish python would have simpler syntax for lambda which is currently using the lambda expression, even JS is doing better on this. A built-in syntax for partial would also be great. It could be even better if we can have function composition.

Some problems are better solved the FP way. It could even make the program more readable which is one of the strengths of Python.



Yes, coming back to Python after many years with Kotlin and Elm, I see how the Python language guides people to write code in a more unreadable way.

Mutations everywhere. But mainly because mapping and flatting stuff is so burdensome. And it lacking many fp functions and a proper way to call them ergonomically makes a crazy list comprehension the goto tool, which often is much less explicit about what's going on than calling a function with a named and widely understood concept.


> And it lacking many fp functions and a proper way to call them ergonomically makes a crazy list comprehension the goto tool, which often is much less explicit about what's going on than calling a function with a named and widely understood concept.

I've said roughly this before somewhere on HN but cannot find it right now: list comprehensions are like regexes. Below a certain point in complexity, they're a much cleaner and immediately-graspable way of expressing what's going on in a given piece of code. Past that point, they rapidly become far worse than the alternative.

For example, "{func(y): y for x, y in somedict.items() if filterfunc(x)}" is clearer than the equivalent loop-with-tempvars, and significantly clearer than "dict(map(lambda k: (func(somedict[k]), somedict[k]), filter(filterfunc, somedict.keys()))))". Even with some better mapping utilities (something like "mapkeys" or "filtervalues", or a mapping utility for dictionaries that inferred keys-vs-values based on the arity of map functions), I think the "more functional" version remains the least easily intelligible.

However, once you cross into nested or simultaneous comprehensions, you really need to stop, drop, and bust out some loops, named functions, and maybe a comment or two. So too with regex! Think about the regex "^prefix[.](\S+) [1-7]suffix$"; writing the equivalent stack of splits and conditionals would be more confusing and easier to screw up than using the regex; below a point of complexity it's a much clearer and better tool to slice up strings. Past a point regexes, too, break down (for me that point is roughly "the regex doesn't fit on a line" or "it has lookaround expressions", but others may have different standards here).


The annoying thing is they have a performance cost though: list comprehensions can be very fast because they stay in the C codepath of Python, whereas unrolled into a loop you lose some of that.


They added "pattern matching" as a statement.

The irrational hatred of FP is still there, to the point of the absurdity of implementing a hobbled procedural version of pattern matching!

It is bordering on insane.


Please suggest a pattern matching expression syntax that doesn’t require completely changing how the language works.

Pattern matching took so long because it was extremely hard to find that compromise, and I actually think they did an okay job. No, it won’t cover every case, but it will also cover the important ones.


  x = match y:
    case 0: "zero"
    case 1: "one"
    case x:
      y = f(x)
      y+y
  
  print(x)
it can require semicolons or parentheses or even braces somewhere if that's necessary to make the grammar work, idc. just don't make me type out `x = ...` a hundred times...

(yes, i know you could use the walrus operator in the third case)

(also, if anyone wants to reply with "use a named function because READABILITY", please save it)


Judging by the votes you got, your proposal is not well-liked.

In general, breaking Python's syntax rules to introduce block expressions or what you want to call it seems like a steep price to pay for what amounts to syntactic sugar in the end.

However, your proposal actually misses the mark in the same way as the actual match/case does as well. What would be /really/ handy in Python is something like

    {'some_key': bind_value_to_this_name} = a_mapping
because it is so common, especially if you're consuming a JSON API. You of course see the myriad problems with the above.

I think you should read the PEPs on match/case, these things have actually been considered. One idea was to introduce a keyword to say which variables are inputs and which are outputs, but that also violates the general grammar in a way that isn't very nice.

The accepted solution has warts, I agree, but at some point you just have to accept that you can't reach some perfect solution.


> Judging by the votes you got, your proposal is not well-liked.

i'm at peace with that :) [1]

> In general, breaking Python's syntax rules to introduce block expressions or what you want to call it seems like a steep price to pay for what amounts to syntactic sugar in the end.

that's your opinion, i disagree! i think Python would be a better, more pleasant to use language if they figured this out. a girl can dream, ok?

> What would be /really/ handy in Python is something like

  {'some_key': bind_value_to_this_name} = a_mapping
yes, extending the destructuring syntax would be nice, i agree! but the original question was "Please suggest a pattern matching expression syntax [...]", and the post you were responding to was specifically talking about `match`'s syntax. [2]

> I think you should read the PEPs on match/case

i read them when they came out. i'm assuming you're referring to this section from PEP 622:

> "In most other languages pattern matching is represented by an expression, not statement. But making it an expression would be inconsistent with other syntactic choices in Python."

https://www.python.org/dev/peps/pep-0622/#id77

i believe Python's statement-orientatation kinda sucks, so to me this is just "things aren't great, let's stay consistent with that". yeah, yeah, "use a different language if you don't like it" etc.

---

[1] well, maybe not quite, after all i'm here arguing about it.

[2] `match` uses patterns like the one you described in `case` arms, but is distinct from them. i don't see why dict-destructuring syntax couldn't be added in a separate PEP.


pedantic addendum:

looks like i messed up my PEPs -- i linked to PEP622, which was superseded by PEP635 [1]. the gist of it hasn't changed though.

---

[1] https://www.python.org/dev/peps/pep-0635/#the-match-statemen..., Ctrl+F "Statement vs. Expression"


I'm interested in similar proposals that can provide the basis of some common syntax that could be transpiled to other functional programming languages https://github.com/adsharma/py2many

I'm less interested in the syntax (will take anything accepted by a modified ast module), more in the semantics.

Here's a syntax I played with in the past:

  def area(s: Shape):
    pmatch(s):
        Circle(c):
            pi * c.r * c.r
        Rectangle(w, h):
            w * h
    ;;

  def times10(i):
    x = pmatch(i):
        1:
            10
        2:
            20
    ;;
    return x
Two notes:

  * Had to be pmatch, since match is more likely to run into namespace collision with existing code.
  * The delimiter `;;` at the end was necessary to avoid ambiguity in the grammar for the old parser. Perhaps things are different with the new PEG parser.
Code is on my github.


Here is pattern matching as a library that is not built using statements,

https://github.com/santinic/pampy

Clearly it's possible. It's also more ergonomic than PEP 622.


Interesting idea, though falls short of FP matching again. The syntax we actually have does a lot more.


Learn You a Haskell for Great Good taught me list comprehension, and I was surprised to see such a pithy shorthand available in Python (+ dict comprehension). That's a very functional way to transform objects from one shape to another with an in-line expression, no lambda keyword there.

But even the lambda keyword isn't so bad, you can create a dictionary of expressions to call by name, a lot more compact them declaring them the usual way imo: https://github.com/jazzyjackson/py-validate/blob/master/pyva...

To your point, I only recently learned there's a Map function in Python, while in JS I'm .map(x=>y).filter(x=>y).reduce(x=>y)ing left and right.


Yes! List/Dictionary/Generator comprehension is one big plus for Python, it probably came from the functional world. I use it whenever I can.

> But even the lambda keyword isn't so bad, you can create a dictionary of expressions to call by name, a lot more compact them declaring them the usual way imo: https://github.com/jazzyjackson/py-validate/blob/master/pyva...

lambda keyword is better than nothing, it definitely can be improved. Just imaging using javascript syntax in your example.

> To your point, I only recently learned there's a Map function in Python, while in JS I'm .map(x=>y).filter(x=>y).reduce(x=>y)ing left and right.

I think with the introduction of list comprehension Guido saw map function was no longer needed, that was why he wanted it removed. I don't deny it, but using map and filter sometimes are just easier to read. Say [foo(v) for v in a] vs map(foo, a).


Yes but map in Python is riduclously slow when map is combined with a lambda [1]

[1]: https://stackoverflow.com/questions/1247486/list-comprehensi...


It's a bit hard to find recent numbers in that decade-old thread, but the relevant, more up to date, evidence I see (in this answer) says that map is not much slower than a list comprehension: https://stackoverflow.com/a/57104206


I agree.

One gripe that I have with functions like map is that it returns a generator, so you have to be careful when reusing results. I fell into this trap a few times.

I'd also like a simpler syntax for closures, it would make writing embedded DSLs less cumbersome.


> One gripe that I have with functions like map is that it returns a generator, so you have to be careful when reusing results

I hope that is never changed; I often write code in which the map function's results are very large, and keeping those around by default would cause serious memory consumption even when I am mapping over a sequence (rather than another generator).

Instead, I'd advocate the opposite extreme: Python makes it a little too easy to make sequences/vectors (with comprehensions); I wish generators were the default in more cases, and that it was harder to accidentally reify things into a list/tuple/whatever.

I think that if the only comprehension syntax available was the one that created a generator--"(_ for _ in _)"--and you always had to explicitly reify it by calling list(genexp) or tuple(genexp) if you wanted a sequence, then the conventions in the Python ecosystem would be much more laziness-oriented and more predictable memory-consumption wise.

Ah well, water under the bridge, I know.


Personally, I mostly just avoid using map/filter and use a list/generator/set/dict comprehension as required. I don't find map(foo, bar) much easier to read than [foo(thingamajig) for thingamajig in bar]


Yes, this is I think why Guido wanted to get rid of map() syntax - he prefers comprehension syntax.


Simply wrap it in list(), or use a list comprehension. Most things in python3 are iterators now, so the same thing applies to many things.


i think thats the most powerful part of the tool. being able to effortlessly write lazy generators is absolutely amazing when working with any sort of I/O system or async code.


> It could be even better if we can have function composition.

I think composition and piping are such basic programming tools that make a lot of code much cleaner. It's a shame they're not built-in in Python.

So, shameless plug, in the spirit of functools and itertools I made the pipetools library [0]. On top of forward-composition and piping it also enables more concise lambda expressions (e.g. X + 1) and more powerful partial application.

[0] https://0101.github.io/pipetools/doc/


Aren't you contradicting yourself a bit?

First you claim functional languages are scary to look at, then say that you want Python to become more like functional languages. But maybe the reason Python is elegant and easier to read is exactly because Guido had the self-constraint to not go full functional. You also miss the part of the story where he actually did remove `reduce` from builtin, exactly because of how unreadable and confusing it is to most.

It's exactly that kind of decision making I expected from a BDFL, and I think Guido did a great job while he was one keeping Python from going down such paths.


Well, the BDFL is probably the only dictator we all love.

Any decision he made is infinitely more than I could. Because I am just a python user, and an outsider in any decision making process. So for me, he's right all the time. That's a perfect definition of a dictator :)

But I do have wishes. It's like I love my parents but I do want to stay up late sometimes.

Yeah, I totally missed the part he removed reduce from builtin. Sorry about my memory. map, filter, or reduce, it does not matter. As I stated, some problems are better solved functional way. Because Python is such a friendly language, if it includes functional paradigm properly, it would make the functional part more readable than other functional languages.

FP is scary not because it has evil syntax to keep people at distance, it's just an alien paradigm to many. Lot's of non functional languages has functional support, which doesn't make them less readable. E.g. C#, JS. I suspect these languages have helped many understanding FP more. Python could make the jump by including more FP, but not turning into a full-fledged FP.

BTW. I'm still glad reduce is kept in functools.


> Because Python is such a friendly language, if it includes functional paradigm properly, it would make the functional part more readable than other functional languages.

I guess my argument is that Python is friendly exactly because it uses FP paradigms sparingly and conscientiously. Some paradigms such as map and filter can definitely make your code cleaner, while others (such as reduce) only lead to headaches. That being said as you mention we are getting pattern matching, I'm curious to see how that ends up.

i would argue that C# and JS do also use it sparingly and don't go too hard, but also that Python is generally a cleaner language than those. It's hard to know how much of it can be attributed to what, but generally, it's not a trivial thing to predict what impacts a given language feature will have on the readability of the code. A given FP paradigm might be cool in isolation, but when stacked with 4 other ones, it can quickly lead to gibberish code.


After giving some more thought on it. I just realized it was python got me into FP. Although the majority my projects are in C#, Linq never had me interested in FP.

You are right, C# and JS use FP sparingly. I think I expect more from Python because I enjoy writing in Python and my functional adventures were rooted in Python.

Personally, lambda expression is painful to look at. Many times I have deliberately avoided it. It's possible to make improvements, but it seems the FP part just stagnated. Looking forward to pattern matching though.


I was going to say something to that effect.

Functional programming is kind of cool unless it's someone else's code and you need to debug it.


I'd much rather debug someone's pure functional code rather than trying to figure out how someone's imperative code got into some unexpected/invalid state.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: