-
-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shorthand for match assignment functions #833
Comments
To keep things consistent for refactoring purposes, you can actually just use
Regardless, I could potentially be sold on #190, but I'm not excited about adding syntax like what you're describing; it's redundant with what already exists and doesn't seem very Pythonic to me. |
The whole point of this library, as seems to me, is to add a bunch of great, non-pythonic features to python from languages such as the ones you specify in the docs:
All of which (Coffeescript with extensions) have a syntax very similar to the one I propose here.
If this is considered redundant then how would it differ from say replacing the lambda syntax with It's your language and I totally respect your freedom to veto features purely based on your opinion, but you did offer some rebuttals here which I argue contradict the rest of the direction of this project. |
Hmmm... I'll keep the issue open in case anyone comes up with a syntax here that I like but currently I just don't really see the benefit of def f(x)
| 0 =
# some code
-1
| _ = x over addpattern def f(0) =
# some code
-1
addpattern def f(x) = x and in fact I think I mildly prefer the latter syntax; having the pattern explicitly in the function signature seems more clean to me. |
Here is an example using the current syntax: data Expr(value)
data Body(exprs)
data Constant(value)
data BinOp(a, op, b)
data Add()
data Sub()
AST = Expr | Body | Constant | BinOp | Add | Sub
addpattern def dump_ast(Expr(value)) = dump_ast(value)
addpattern def dump_ast(Body(exprs)) = exprs |> map$dump_ast |> "\n".join
addpattern def dump_ast(Constant(value)) = str(value)
addpattern def dump_ast(BinOp(a, op, b)) = (a,op,b) |> map$dump_ast |> " ".join
addpattern def dump_ast(Add()) = "+"
addpattern def dump_ast(Sub()) = "-"
ast = Body([
Expr(BinOp(Constant(2), Sub(), Constant(1))),
Expr(BinOp(Constant(2), Add(), Constant(3))),
])
print(dump_ast(ast)) In the above example, there are a few issues:
The suggested syntax would resolve all of these issues: def dump_ast(node: AST) -> str
| Expr(value) = dump_ast(value)
| Body(exprs) = exprs |> map$dump_ast |> "\n".join
| Constant(value) = str(value)
| BinOp(a, op, b) = (a,op,b) |> map$dump_ast |> " ".join
| Add() = "+"
| Sub() = "-" |
The repetition point is well-taken. I'll put this on the next milestone to consider what the best thing might be to do here. |
Here's a first-pass proposal case def func: <typedef>
match (<pattern>):
<body> which would make your function case def dump_ast: AST -> str
match(Expr(value)) = dump_ast(value)
match(Body(exprs)) = exprs |> map$dump_ast |> "\n".join
match(Constant(value)) = str(value)
match(BinOp(a, op, b)) = (a,op,b) |> map$dump_ast |> " ".join
match(Add()) = "+"
match(Sub()) = "-" or another example case def factorial[Num <: int | float]: (Num, Num) -> Num
"""Factorial function"""
match(0, acc=1):
return acc
match(int(n), acc=1) if n > 0:
return factorial(n - 1, acc * n) |
Okay, the above syntax is working now as of |
Biggest thing I'm currently unhappy with here is that it doesn't give you nice typing. It'd be nice if there was a way for this to translate straightforwardly into |
I don't quite follow. Shouldn't the match sub-functions that use this syntax stay consistent with the typing specified in the function definition, i.e. dynamically determining the type would essentially take the union of the sub-function types? Or do you mean the type hinting would change based on which match statement is chosen assuming it can be statically determined? |
I'd ideally like a way to separately type each case in such a way that the different signatures can be combined with if TYPE_CHECKING:
import typing
@typing.overload
def my_min[T](xs: T[]) -> T = ...
@typing.overload
def my_min[T](x: T, y: T) -> T = ...
else:
addpattern def my_min([x]) = x
addpattern def my_min([x] + xs) = my_min(x, my_min(xs))
addpattern def my_min(x, y if x <= y) = x
addpattern def my_min(x, y) = y My current best idea for this is to find a way to make this work with the protocol intersection operator ( case def my_min[T]: T[] -> T &: (T, T) -> T
match([x]) = x
match([x] + xs) = my_min(x, my_min(xs))
match(x, y if x <= y) = x
match(x, y) = y |
Okay, here's a new proposal: case def my_min[T]:
type(xs: T[]) -> T
match([x]) = x
match([x] + xs) = my_min(x, my_min(xs))
type(x: T, y: T) -> T
match(x, y if x <= y) = x
match(x, y) = y You're allowed to have as many Also, one thing I can't decide is whether guards should go inside or outside the parentheses. Inside makes it closer to pattern-matching function definition, while outside makes it closer to normal |
One last question worth considering here: should the keyword in the body to denote a pattern-matching case be |
I think match def Fact:
case 0 = 1
case int(x) = x * Fact(x-1) is more consistent with the original python syntax. Since the first level of indentation is guaranteed to be a case rather than a statement, I think a keyword could be considered redundant: match def Fact:
0 = 1
int(x) = x * Fact(x-1) |
@MatthiasJ1 The top-level keyword has to be |
See Coconut's [documentation](http://coconut.readthedocs.io/en/develop/DOCS.html) for more information on all of the features listed below. Language features: * #833: New `case def` syntax for more easily defining pattern-matching functions with many patterns. * #811: New `f(name=)` syntax as a shorthand for `f(name=name)`, replacing the now deprecated `f(...=name)` syntax. * #836: New `CoconutWarning` built-in used for Coconut runtime warnings. Compiler features: * #837: Coconut will now warn about implicit string concatenation and disable it completely with `--strict`. * #718: Coconut will now warn about use of `addpattern def` without a prior `match def`. This was a previously-supported feature to make pattern-matching functions with many patterns easier to write, but the new recommended way to do that is now via `case def`. * #785: Initial [pyright](https://github.com/microsoft/pyright) support via the `--pyright` flag. Bugfixes: * #839, #840: Fixed some f-string parsing issues. * #834: Fixed `len` of empty `zip` objects. * #830: Improved use of colored output. * #757: Improved PEP 695 support on Python 3.12.
A common paradigm in functional programming is a function whose implementation is determined by matching the input. This is supported in coconut by either
addpattern
functions or traditional match statements.addpattern
can be unnecessarily verbose, especially if you like descriptive function names. This also makes refactoring more annoying as now you have to rename the function multiple times.Traditional match statements do not work for assignment functions (#832):
Even if this worked, it would be overly verbose compared to the rest of Coconut which has very streamlined, terse syntax.
I propose a syntax similar to Haskell or StandardML
The text was updated successfully, but these errors were encountered: