Poposal: Syntactic Sugar - Using recursion (with pattern matching) #1788
Replies: 13 comments
-
I think this is a duplicate of #1548, where Neal Gafter commented:
|
Beta Was this translation helpful? Give feedback.
-
There have been a couple of requests for overloading by pattern matching. The sentiment from the team has been generally negative for two reasons. First, because it embeds implementation into the signature of the method. Second, because it results in a run-time match expression which cannot be exhaustively guaranteed to succeed, and when matching fails that will result in a run-time error. This is in stark contrast to overloading which is resolved at compile time. |
Beta Was this translation helpful? Give feedback.
-
Well, for me that construct is really nice and the only thing I currently miss in C# from other languages :) I don't get a part of the problem from HaloFour, but since you discussed already in depth about it, I probably just miss it or don't have all information at hand (which is probably always the case, if you do not understand the other side ^^). |
Beta Was this translation helpful? Give feedback.
-
In Haskell those values in the argument lists are patterns, which are matched at runtime. If you forget to include an "overload" to handle every potential value that will result in an error at runtime. Your specific use case of a single value and a catch-all is safe, but it's very easy to end up in a situation where your matching logic is not exhaustive and something could slip through the cracks. In either case, "overloading" between Haskell and C# are paradigmatically and irreconcilably different. Having top-level signatures that would collapse into a single proper method with an embedded runtime match doesn't fit into the way C# works. |
Beta Was this translation helpful? Give feedback.
-
factorial 0 = 1
factorial n = n * factorial (n - 1) I don't find that easier to read at all. I have no idea what that does. As far as I'm concerned int Foo(int n) => n * Foo(n - 1); and the shorthand was something like: int Foo(int n) => 0 = 1; What would I have to change to get the shorthand version of anyone of these methods: int Foo(int n) => n * Foo(n - 2);
int Foo(int n) => n * Foo(n - 1) + 1;
int Foo(int n) => (n + 1) * Foo(n - 1) + (n - 2) * 3; And what would these shorthand versions do: int Foo(int n) => 0 = 2;
int Foo(int n) => 2 = 0;
int Foo(int n) => 2 * n = 0; I don't see how there'd be any consistent way of predicating the code, and recursion is already hard enough to wrap the mind around when they're written in plain English. Maybe if you could provide some C# syntax examples it would make more since as I don't know Haskell at all. Right now, I think it's just way to much happening behind the scenes. |
Beta Was this translation helpful? Give feedback.
-
I agree with you @AustinBryan that C# probably doesn't need recursive patterns ... but I can take a stab at what potential syntax might look like ... Assuming my (small) understanding of Haskel is correct, then this
Might translate to something like this
(Reusing the existing when keyword to define the condition.) I believe that Haskel is explicitly sensitive to the order of declarations (that is, the degenerate case must come first); up until now, the order of method declarations in C# hasn't been significant, so that would be a big change. Unfortunately, I don't understand your other examples well enough to have a stab at translation into this style. Perhaps someone else will ... |
Beta Was this translation helpful? Give feedback.
-
@theunrepentantgeek It probably will look something more like how you described, which isn't that huge of a gain from actually typing it out, so it's just obscuring the actual code and, unless there's some method to the madness, we'd have to memorize what all of the shorthands translate to. To me, it just makes it even harder to wrap my head around. If I saw that, I'd have to ask what it actually did, which would waste time, or they'd have to comment it, which defeats the point. Again, that's only if there isn't some learnable method to it, which I'm sure there is, it's just that benefit from everyone having to learn it just to save a few characters is very marginal. #1548 also proposed having the order matter when how you define methods, which I really dislike. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour: Well ok, if you don't make the restriction, that you need a catch-all, it can get messy. @AustinBryan It is closer to the mathematical representation, which makes the first steps in C# easier, if your understanding of recursion comes from there. In general, it is pretty close, how you write a
0 would be an overload of the function Factorial. So, 0 would be part of the signature. Or what theunrepentantgeek, while I am not that big of a fan of that syntax, but well. That is perhaps just me. But thanks for taking the time. I get your points. While I am still thinking, that those could be worked out, you probably just told me some of the things, why you decided against the concept. At the end I believe, that you know what you are doing and if you considered it, that is enough for me. |
Beta Was this translation helpful? Give feedback.
-
That would not work. Which overload is called is decided at compile-time, so in this case, |
Beta Was this translation helpful? Give feedback.
-
@NicMay Typo |
Beta Was this translation helpful? Give feedback.
-
int Factorial(int n) => (n == 0) ? 1 : n * Factorial(n - 1); |
Beta Was this translation helpful? Give feedback.
-
@NicMay That makes a lot more since in C# terms because before I didn't even know that you were using an overload, or parameters, and I wasn't even sure if factorial was a keyword or the name of the function. 😅 Still, @VanKrock's solution is much better. Just use the conditional operator. It's more intuitive from people coming from C#, C/C++, Java, Python, Ruby, Lua, etc., all the many languages with the conditional operator. And, best part, it's shorter than the other suggestions so far. I've put each suggestion on one line so it's easier to see their lengths: // theunrepentantgeek's suggestion
int Factorial(int n) when (n == 0) => 1; int Factorial(int n) => n * Factorial(n - 1);
// Your suggestion
int Factorial(int 0) => 1; int Factorial(int n) => n * factorial (n - 1);
// Current C# (and many other languages)
int Factorial(int n) => (n == 0) ? 1 : n * Factorial(n - 1);
// Haskell
factorial 0 = 1 factorial n = n * factorial (n - 1) And yes, Haskell's is the shorters, but that's because it doesn't have the return type, no parens, semi colons, or the type of the parameters. All of these are things that C# will never drop so you're suggestion is basically as short as it can possibly get, while it's most likely going to be more like @theunrepentantgeek's and make use of the Also, it being closer to mathamatics is kinda like judging a movie based off of how well it stuck to the book. My sister hates Harry Potter 3 when it's one of my favorites only because it didn't stick to the book. A movie being good is completely independent of whether or not it stuck to the book, and I feel that's the same with programming languages sticking to the original mathematics. There are other languages that feel much more mathematical, and C# isn't really one of them. I don't think it would make sense to have some syntax that looks so different from the rest of C#. |
Beta Was this translation helpful? Give feedback.
-
It's also not more understandable. When looking at your "int Factorial(int n)" there is nothing even indicating the existence of a base case(It would be elsewhere in the file, but any number of things could be located between the definitions and a computer screen is only so big) which would lead to me as a reader to think the method is wrong. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
it would fit to the functional way C# is heading to. Haskell or Elixir support it for example. You can overload a method by values not just the type. Example from Haskell (factorial is in this case a function and the definition is behind the equal sign):
The main benefit is, that recursive methods are easier to read. You have to create a method for the n'th step and then a method for the recursion end, both are seperated and easier to reason about.
A possible solution would be to convert that expression to a switch statement in that specific method.
Beta Was this translation helpful? Give feedback.
All reactions