Following on from PEP 634, it would be good to be able to have Erlang / Elixir-style pattern matching in function headers. (I don't know what the technical term is for this language feature, but hopefully the examples will clarify.) Here's the obligatory fibonacci example: ``` def fib(0): return 0 def fib(1): return 1 def fib(n): return fib(n-1) + fib(n-2) ``` Or, another example: ``` def allow_entry({"name": "Bob"}): return "Bob is not allowed in ever!" def allow_entry({"name": "Charles", "day": "Tuesday"}): return "It's a Tuesday, so Charles is allowed in." def allow_entry({"name": "Charles", "day": _}): return "Charles is only allowed in on a Tuesday." def allow_entry({"name": name}): return f"Come in {name}, make yourself at home!" ``` Thanks for considering my idea. Kind regards Sam Frances
I assume that such a feature would simply behave exactly the same as a match statement: def fib(arg): match arg: case 0: return 0 case 1: return 1 case n: return fib(n-1) + fib(n-2) # I know this is only a trivial example, # but you should probably also handle # n < 0. Does your proposal cover using # guards, or would they need a conditional # in the final case? def allow_entry(arg): match arg: case {"name": "Bob"}: return "Bob is not allowed in ever!" case {"name": "Charles", "day": "Tuesday"}: return "It's a Tuesday, so Charles is allowed in." case {"name": "Charles", "day": _}: return "Charles is only allowed in on a Tuesday." case {"name": name}: return f"Come in {name}, make yourself at home!" # I only skimmed the PEP, and I don't have a copy of Python 3.10 to check, # but I believe that if arg doesn't match any of the given clauses, the match # silently does nothing. I don't know if that was your expected behaviour for # the definition form, but you should probably be explicit about what you intend # in any case. Note that I created these by taking your examples and applying a purely mechanical translation - I didn't think about it at all, so this transformation could easily be applied mechanically. What's the benefit that would justify having this additional syntax? In addition, you'd need to consider the implications of possibilities you didn't cover in your examples. I don't know Elixir, so I can't say whether that language has similar scenarios that we could follow, but these are all valid in Python, so we need to consider them. Python's function definitions are executed at runtime, so you need to decide what behaviour you want from a function definition that's had *some* of its parts executed, but not others. So how would the following behave? def example(1): return "One" print(example(2)) def example(2): return "Two" print(example(2)) Worse, what if the example(2) definition were in a separate module? What about decorators? def example(1): return "One" @some_decorator def example(2): return "Two" That's just a very brief list of "things to think about". In functional languages, I like this style of function definition, but it would need some fairly careful design to be able to translate it to a language like Python. And honestly, I'm not sure I see the advantage (in Python). I'm not against the suggestion as such, but I think it will need a fair amount of work to flesh it out from a series of basic examples to a full-fledged proposal (which even then might end up getting rejected). Is that something you're considering doing yourself, or are you just offering the idea in case someone else is interested in picking it up? Paul On Thu, 5 Aug 2021 at 09:52, Sam Frances <sam@samfrances.uk> wrote:
Following on from PEP 634, it would be good to be able to have Erlang / Elixir-style pattern matching in function headers.
(I don't know what the technical term is for this language feature, but hopefully the examples will clarify.)
Here's the obligatory fibonacci example:
``` def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2) ```
Or, another example:
``` def allow_entry({"name": "Bob"}): return "Bob is not allowed in ever!"
def allow_entry({"name": "Charles", "day": "Tuesday"}): return "It's a Tuesday, so Charles is allowed in."
def allow_entry({"name": "Charles", "day": _}): return "Charles is only allowed in on a Tuesday."
def allow_entry({"name": name}): return f"Come in {name}, make yourself at home!" ```
Thanks for considering my idea.
Kind regards
Sam Frances _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/OPLBLW... Code of Conduct: http://python.org/psf/codeofconduct/
All good points. To me there is an elegance in being able to split the different paths of the function into their own syntactically separate function definitions. One place where this excels is in handling happy paths vs error paths. It increases clarity substantially, in my experience, to be able to code the happy path without separately from the error path. ``` def foo("error") # do some error handling def foo(n): # code the happy path ``` Whether this is worth the extra syntax, as you put it, I'm not sure. I don't think I have the knowledge to write a PEP that has a reasonable chance of adoption, so at this point I'm just "offering the idea". On 05/08/2021 10:24, Paul Moore wrote:
I assume that such a feature would simply behave exactly the same as a match statement:
def fib(arg): match arg: case 0: return 0 case 1: return 1 case n: return fib(n-1) + fib(n-2) # I know this is only a trivial example, # but you should probably also handle # n < 0. Does your proposal cover using # guards, or would they need a conditional # in the final case?
def allow_entry(arg): match arg: case {"name": "Bob"}: return "Bob is not allowed in ever!" case {"name": "Charles", "day": "Tuesday"}: return "It's a Tuesday, so Charles is allowed in." case {"name": "Charles", "day": _}: return "Charles is only allowed in on a Tuesday." case {"name": name}: return f"Come in {name}, make yourself at home!" # I only skimmed the PEP, and I don't have a copy of Python 3.10 to check, # but I believe that if arg doesn't match any of the given clauses, the match # silently does nothing. I don't know if that was your expected behaviour for # the definition form, but you should probably be explicit about what you intend # in any case.
Note that I created these by taking your examples and applying a purely mechanical translation - I didn't think about it at all, so this transformation could easily be applied mechanically. What's the benefit that would justify having this additional syntax?
In addition, you'd need to consider the implications of possibilities you didn't cover in your examples. I don't know Elixir, so I can't say whether that language has similar scenarios that we could follow, but these are all valid in Python, so we need to consider them.
Python's function definitions are executed at runtime, so you need to decide what behaviour you want from a function definition that's had *some* of its parts executed, but not others. So how would the following behave?
def example(1): return "One"
print(example(2))
def example(2): return "Two"
print(example(2))
Worse, what if the example(2) definition were in a separate module?
What about decorators?
def example(1): return "One"
@some_decorator def example(2): return "Two"
That's just a very brief list of "things to think about". In functional languages, I like this style of function definition, but it would need some fairly careful design to be able to translate it to a language like Python. And honestly, I'm not sure I see the advantage (in Python).
I'm not against the suggestion as such, but I think it will need a fair amount of work to flesh it out from a series of basic examples to a full-fledged proposal (which even then might end up getting rejected). Is that something you're considering doing yourself, or are you just offering the idea in case someone else is interested in picking it up?
Paul
On Thu, 5 Aug 2021 at 09:52, Sam Frances <sam@samfrances.uk> wrote:
Following on from PEP 634, it would be good to be able to have Erlang / Elixir-style pattern matching in function headers.
(I don't know what the technical term is for this language feature, but hopefully the examples will clarify.)
Here's the obligatory fibonacci example:
``` def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2) ```
Or, another example:
``` def allow_entry({"name": "Bob"}): return "Bob is not allowed in ever!"
def allow_entry({"name": "Charles", "day": "Tuesday"}): return "It's a Tuesday, so Charles is allowed in."
def allow_entry({"name": "Charles", "day": _}): return "Charles is only allowed in on a Tuesday."
def allow_entry({"name": name}): return f"Come in {name}, make yourself at home!" ```
Thanks for considering my idea.
Kind regards
Sam Frances _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/OPLBLW... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Aug 06, 2021 at 09:30:57AM +0100, Sam Frances wrote:
All good points. To me there is an elegance in being able to split the different paths of the function into their own syntactically separate function definitions.
Do you use `functools.singledispatch`? Thatt gets you part-way there.
One place where this excels is in handling happy paths vs error paths. It increases clarity substantially, in my experience, to be able to code the happy path without separately from the error path.
I don't know... it seems to me that the error path is generally going to be a single statement, `raise SomeException(message)`. Does that really need to be its own function? It might help if you show some real code rather than trivial examples like recursive fibonacci and made-up toy functions. -- Steve
Consider ML-style? :D ``` def fib(n): | n == 0 = 0 | n == 1 = 1 | otherwise = fib(n-1) + fib(n-2) def allow_entry(d): | d == {"name": "Bob"} = "Bob is not allowed in ever!" | d == {"name": "Charles", "day": "Tuesday"} = \ "It's a Tuesday, so Charles is allowed in." | d == {"name": "Charles", "day": _}) = \ "Charles is only allowed in on a Tuesday." | d == {"name": name} = f"Come in {name}, make yourself at home!" ``` On 05/08/2021 16:39, Sam Frances wrote:
Following on from PEP 634, it would be good to be able to have Erlang / Elixir-style pattern matching in function headers.
(I don't know what the technical term is for this language feature, but hopefully the examples will clarify.)
Here's the obligatory fibonacci example:
``` def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2) ```
Or, another example:
``` def allow_entry({"name": "Bob"}): return "Bob is not allowed in ever!"
def allow_entry({"name": "Charles", "day": "Tuesday"}): return "It's a Tuesday, so Charles is allowed in."
def allow_entry({"name": "Charles", "day": _}): return "Charles is only allowed in on a Tuesday."
def allow_entry({"name": name}): return f"Come in {name}, make yourself at home!" ```
Thanks for considering my idea.
Kind regards
Sam Frances _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/OPLBLW... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Aug 06, 2021 at 04:17:55PM +0800, Mxzixl wrote:
Consider ML-style? :D
```
def fib(n): | n == 0 = 0 | n == 1 = 1 | otherwise = fib(n-1) + fib(n-2)
I like the look of this when I read Haskell code. We can bike-shed the syntax, but I think that's more promising than multiple function definitions. -- Steve
On Thu, Aug 05, 2021 at 09:39:44AM +0100, Sam Frances wrote:
Following on from PEP 634, it would be good to be able to have Erlang / Elixir-style pattern matching in function headers.
This was recently discussed here: https://mail.python.org/archives/list/python-ideas@python.org/message/ROYZNX...
def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2)
I think that there is something rather disturbing about writing a function definition with a constant literal as parameter. It looks wrong and I'm sure it's going to confuse beginners. Let us remember that Python's execution model is not the same as Erlang or Elixir. In Python, when you write: def fib(a): ... def fib(b): ... that is not a single compile-time declaration, it is a pair of run-time statements. In pseudo-code: # create a function, bind it to the name "fib" fib = make_function( ... ) # create a new function, bind it to the same name, # garbage collecting the first function fib = make_function( ... ) So we would need a radical and major change in the execution model of Python to turn your example into a single function using pattern- matching multiple-dispatch. If we did allow the use of literals as parameters, what would happen if we ran some other code in between the cases of the function? def fib(0): return 0 print("suprise!") def fib(1): return 1 We would, apparently, have two distinct modes: - sometimes re-defining a function replaces the previous binding; - but other times, it extends the function with a new case. What are the rules for each? The nice thing about Python is that we (mostly) have a very predictable, consistent execution model. Syntax is not generally context-dependent, so you can usually interpret the meaning of code by considering it in isolation, rather than by tracking the context. (With a few simple modifiers, such as `global`.) For example: def func(arg): ... creates a function object named "func", and binds it to the name "func" in the local namespace. That applies whether the def statement is inside a class, nested inside another function, in the top-level module, inside a for-loop or if-statement, etc. Because of that consistent behaviour, if we want to use generic functions, we use the singledispatch decorator. This has the advantage of making it explicit and obvious that we're doing generic functions.
def allow_entry({"name": "Bob"}): return "Bob is not allowed in ever!"
I don't know how to interpret that syntax. Does that mean `allow_entry` takes a single argument, which much be a dictionary with a single key, 'name'? result = allow_entry({'name': 'Bob'}) assert result = "Bob is not allowed in ever!" If that's not what you mean, then how would I write a function that *actually does* literally match the dict `{'name': 'Bob'}`? -- Steve
On Fri, Aug 6, 2021 at 9:59 PM Steven D'Aprano <steve@pearwood.info> wrote:
def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2)
I think that there is something rather disturbing about writing a function definition with a constant literal as parameter. It looks wrong and I'm sure it's going to confuse beginners.
Agreed, but that's because Python behaves differently. But we already have a way to have a function-ish thing that then changes behaviour: a property's .setter/.deleter modifiers. Borrowing an idea from that: @multidispatch def fib(n): return fib(n-1) + fib(n-2) @fib.specialize(n=0) def fib(n): return 0 @fib.specialize(n=1) def fib(n): return 1 I'm not really a fan, but (a) it does avoid the "literal as parameter" problem, and (b) it fits Python's syntax.
If we did allow the use of literals as parameters, what would happen if we ran some other code in between the cases of the function?
def fib(0): return 0
print("suprise!")
def fib(1): return 1
We would, apparently, have two distinct modes:
- sometimes re-defining a function replaces the previous binding;
- but other times, it extends the function with a new case.
What are the rules for each?
Agreed. Use of a dedicated specialization operation would avoid this problem. Obviously you wouldn't normally want to do it, but if you did, the behaviour in between those steps would be well-defined: specializations not yet applied would not be used, and the function would execute using the generic form. (Also, I don't think you'd ever want to do it, but you could have a specialization in a different scope from the generic one. The decorator would attach the specialization to the generic.)
def allow_entry({"name": "Bob"}): return "Bob is not allowed in ever!"
I don't know how to interpret that syntax. Does that mean `allow_entry` takes a single argument, which much be a dictionary with a single key, 'name'?
result = allow_entry({'name': 'Bob'}) assert result = "Bob is not allowed in ever!"
If that's not what you mean, then how would I write a function that *actually does* literally match the dict `{'name': 'Bob'}`?
Presumably the behaviour would be the same as the match statement. https://www.python.org/dev/peps/pep-0622/#mapping-patterns And that's where any proposal based on current syntax is going to fall down. It's easy enough to build something like singledispatch, but much much harder to make something that can handle the full flexibility of match statements. So you're left with two options: a simpler version that lets you build up the patterns from multiple functions, or the full-power version that has a single master function that can do the dispatching. To be quite honest, I would usually just write it as a single function with a match statement in it. But there are definitely times when I've wanted to be able to build up pattern matching in a more declarative way (think of what can be done with Flask's request routing, for instance), and maybe there'd be enough to justify something like that. ChrisA
On 2021-08-06 at 21:57:47 +1000, Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, Aug 05, 2021 at 09:39:44AM +0100, Sam Frances wrote:
def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2)
I think that there is something rather disturbing about writing a function definition with a constant literal as parameter. It looks wrong and I'm sure it's going to confuse beginners.
You are not a beginner (nor am I); what's disturbing to you may or may not look wrong or confuse someone else. When I was a beginner, x = x + 1 was disturbing, wrong, and confusing to me. The proposed definition of the Fibonacci sequence mirrors the ones in Wikipedia¹ and OEIS,² and will certainly be at least familiar to those with a background in mathematics or coming to Python from functional languages. That said, I agree that it's not a good fit for Python, for reasons expressed elsewhere in this thread. FWIW, Lisp Flavored Erlang³ (a syntactically imperative language built atop the Erlang VM and run-time) doesn't repeat the "def," only the argument lists: lfe> (defun ackermann ((0 n) (+ n 1)) ((m 0) (ackermann (- m 1) 1)) ((m n) (ackermann (- m 1) (ackermann m (- n 1))))) Something like that might work in Python, but at that point, it's no different from a function that matches on its *args argument. ¹ https://en.wikipedia.org/wiki/Fibonacci_number ² https://oeis.org/A000045 ³ https://lfe.io/
For me, it looks normal because I am used to seeing pattern matching for function parameters in functional programming languages. However, if we write “case” before “def” similar to “async” before “def” in async function it should be clear we are doing pattern matching. The function will be named case function. case def fib(0): return 0 case def fib(1): return 1 case def fib(int(n)): return fib(n-1) + fib(n-2) If you none of the parameters you pass match your case functions, an exception will be raised. Maybe it would be easier to make those functions faster since we already know the types in advance. Another solution would be something like this …. def fib(n): case 0, : return 0 case 1, : return 1 case int(n), : return fib(n-1) + fib(n-2) As opposed to … def fib(*args): match args: case 0, : return 0 case 1, : return 1 case int(n), : return fib(n-1) + fib(n-2)
On 6 Aug 2021, at 4:46 PM, 2QdxY4RzWzUUiLuE@potatochowder.com wrote:
On 2021-08-06 at 21:57:47 +1000, Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, Aug 05, 2021 at 09:39:44AM +0100, Sam Frances wrote:
def fib(0): return 0
def fib(1): return 1
def fib(n): return fib(n-1) + fib(n-2)
I think that there is something rather disturbing about writing a function definition with a constant literal as parameter. It looks wrong and I'm sure it's going to confuse beginners.
You are not a beginner (nor am I); what's disturbing to you may or may not look wrong or confuse someone else. When I was a beginner, x = x + 1 was disturbing, wrong, and confusing to me.
The proposed definition of the Fibonacci sequence mirrors the ones in Wikipedia¹ and OEIS,² and will certainly be at least familiar to those with a background in mathematics or coming to Python from functional languages.
That said, I agree that it's not a good fit for Python, for reasons expressed elsewhere in this thread.
FWIW, Lisp Flavored Erlang³ (a syntactically imperative language built atop the Erlang VM and run-time) doesn't repeat the "def," only the argument lists:
lfe> (defun ackermann ((0 n) (+ n 1)) ((m 0) (ackermann (- m 1) 1)) ((m n) (ackermann (- m 1) (ackermann m (- n 1)))))
Something like that might work in Python, but at that point, it's no different from a function that matches on its *args argument.
¹ https://en.wikipedia.org/wiki/Fibonacci_number ² https://oeis.org/A000045 ³ https://lfe.io/ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/TCBZOF... Code of Conduct: http://python.org/psf/codeofconduct/
Abdulla Al Kathiri writes:
case def fib(0): return 0
This syntax (1) is a new, slightly shorter version of something we can already do as you point out yourself, and (2) plausibly is something of a bug magnet if someone does case def fib(1): case def fib(2): def fib(n): Plausible != probable, but given Python's general conservatism vs. syntax, I suspect "this bug is improbable" is on you to argue. Notes: These comments are not original to me. I do agree that this is a plausible syntax, and don't agree with Steven d'A's suggestion that it might be offputting to new Pythonistas -- I think it could go either way if implemented. I just don't think it's particularly Pythonic or beneficial to Python, so -1.
If you none of the parameters you pass match your case functions, an exception will be raised. Maybe it would be easier to make those functions faster since we already know the types in advance.
This claim needs to be supported, and it kinda seems unlikely since we already have def fib(n : int) -> int: (although the optimizer doesn't use such information yet AFAIK).
def fib(n): case 0, : return 0 case 1, : return 1 case int(n), : return fib(n-1) + fib(n-2)
As opposed to …
def fib(*args): match args: case 0, : return 0 case 1, : return 1 case int(n), : return fib(n-1) + fib(n-2)
Saving one line and one level of indentation doesn't seem worth new syntax. This kind of claim is easy to address (again, given Python's conservatism I think it's on the proponents, not the opponents) by going through the stdlib or some other large codebase (Numpy or Pandas or requests might be good candidates) and showing that in fact such dispatching styles would likely bleed off the right edge in many cases where the project has a tight column limit (such as Python's 79 column convention, which is much tighter than many Python programmers like). By the way, what's with the rather strange syntax? In Python 3.10rc1, def fib(n): match n: case 0: return 0 case 1: return 1 case n if isinstance(n, int) and n > 1: return fib(n-1) + fib(n-2) case _: raise RuntimeError is how I'd write it, although I'm not sure that reusing 'n' that way is good style (and RuntimeError is the wrong exception but I'm too lazy to look up the right spelling for args-out-of-range in Python). Interestingly, I can't find a definition for n that gets fib(1.0) to error, except by prepending a "case float()". Not sure if that is intended, anybody know? Steve
On 2021-08-08 at 11:30:20 +0400, Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
... if we write “case” before “def” similar to “async” before “def” in async function it should be clear we are doing pattern matching. The function will be named case function.
case def fib(0): return 0
case def fib(1): return 1
case def fib(int(n)): return fib(n-1) + fib(n-2)
If you none of the parameters you pass match your case functions, an exception will be raised ...
Because the order of cases is significant, these would be difficult to work with in the REPL. If, for example, I start with your three definitions, and then decide to write a special case¹ for 2, how do I convince Python that case def fib(2): return 1 belongs before the general case? ¹ Yeah, I know, special cases aren't special enough. In a non-toy case (pun intended), there could be any number of reasons to add a new case in the middle, or to redfine/fix a case that already exists rather than add a new case to the end.
If you are going to add a new case function somewhere in the middle of a REPL session because order matters, maybe break it with anything like write a number or something.. and then repeat the case functions with the order you wish. Now the new case functions will overwrite whatever you wrote before.. We can make a rule that the case functions should be below each other. I’ve always seen the different functions done above each other in Haskell.
On 8 Aug 2021, at 5:10 PM, 2QdxY4RzWzUUiLuE@potatochowder.com wrote:
Because the order of cases is significant, these would be difficult to work with in the REPL. If, for example, I start with your three definitions, and then decide to write a special case¹ for 2, how do I convince Python that
case def fib(2): return 1
belongs before the general case?
¹ Yeah, I know, special cases aren't special enough. In a non-toy case (pun intended), there could be any number of reasons to add a new case in the middle, or to redfine/fix a case that already exists rather than add a new case to the end.
On Sun, Aug 08, 2021 at 11:30:20AM +0400, Abdulla Al Kathiri wrote:
For me, it looks normal because I am used to seeing pattern matching for function parameters in functional programming languages.
Can you give us a few samples from other languages? The only one I know is Haskell: fib 0 = 0 fib 1 = 1 fib n = fib (n-1) + fib (n-2) I don't *hate* that in Haskell, because Haskell is meant to be read almost as mathematical definitions. But Python functions are declarative, and Python code consists of statements which are executed in a defined order. I don't think that's a good match for Haskell-style syntax. What's wrong with the straight-forward application of a match statement inside the function body? As you suggest:
def fib(*args): match args: case 0, : return 0 case 1, : return 1 case int(n), : return fib(n-1) + fib(n-2)
It costs one extra line (the match statement), but makes it explicit what we're matching. And you save having to repeat the `def fib` for every case. Most importantly, it means that we only need one kind of pattern matching syntax, instead of two. -- Steve
You have a point, but the mathematical approach of Haskell of different function cases is quite readable and straight forward. I only coded a little in Haskell and I know how to read it. I am not sure if you would consider Rust a functional language but they have pattern matching for function parameters but only you’re allowed to use one function signature unlike Haskell. Sent from my iPhone
On 10 Aug 2021, at 10:53 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Aug 08, 2021 at 11:30:20AM +0400, Abdulla Al Kathiri wrote:
For me, it looks normal because I am used to seeing pattern matching for function parameters in functional programming languages.
Can you give us a few samples from other languages? The only one I know is Haskell:
fib 0 = 0 fib 1 = 1 fib n = fib (n-1) + fib (n-2)
I don't *hate* that in Haskell, because Haskell is meant to be read almost as mathematical definitions. But Python functions are declarative, and Python code consists of statements which are executed in a defined order. I don't think that's a good match for Haskell-style syntax.
What's wrong with the straight-forward application of a match statement inside the function body? As you suggest:
def fib(*args): match args: case 0, : return 0 case 1, : return 1 case int(n), : return fib(n-1) + fib(n-2)
It costs one extra line (the match statement), but makes it explicit what we're matching. And you save having to repeat the `def fib` for every case.
Most importantly, it means that we only need one kind of pattern matching syntax, instead of two.
-- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WCOU56... Code of Conduct: http://python.org/psf/codeofconduct/
Abdulla Al Kathiri writes:
You have a point, but the mathematical approach of Haskell of different function cases is quite readable and straight forward.
You seem to have quite missed that point, though. The point is that although borrowing tech from Haskell for Python is a *great* idea (almost as yuge as "namespaces" ;-), Python is not Haskell, and will do those things its own way. In this case we already have a perfectly good syntax for this, it's just that it occurs in the explicit match statement rather than implicitly when a function is defined multiple times. The fact that the Haskell style is often less verbose is likely to carry zero weight. The fact that it means two ways of doing the same thing (one being more general and flexible), is a potential detriment, although not always an idea killer. Also, arguing that "other languages do this and it's fine for them" doesn't help here for syntax -- it frequently does carry weight for the feature itself (Haskell was mentioned more than once in advocating some kind of match statement or expression). You need to argue either that "this is a common (enough) thing to do and it's so verbose that this much more concise way of expressing it is much more readable" or that "this is something that Python can't currently do without [some horrible hack], and it can't really be expressed in a function, but this syntax looks very natural in a Python program and does the job". Nobody will hate on you for disagreeing with all that, but you'll be more able to get things done if you present syntax proposals in those ways. Steve
participants (8)
-
2QdxY4RzWzUUiLuE@potatochowder.com
-
Abdulla Al Kathiri
-
Chris Angelico
-
Mxzixl
-
Paul Moore
-
Sam Frances
-
Stephen J. Turnbull
-
Steven D'Aprano