-
Notifications
You must be signed in to change notification settings - Fork 205
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
Extensible pattern-matching #1047
Comments
This comment was originally written by [email protected] Hopefully this could also be unified with the pattern matching of exceptions in the 'catch' construct. |
We may act on this in the future. Set owner to @gbracha. |
Added Area-Language label. |
Added this to the Later milestone. |
This comment was originally written by @seaneagan issue dart-lang/sdk#3722 proposes to have the "match" keyword above accept any Predicate defined as: typedef bool Predicate<T>(T item); |
This comment was originally written by @seaneagan Also, for type patterns currently we are stuck with doing "new isInstanceOf<int>" or "new isInstanceOf<BadNumberFormatException>". If types were first class and extended Predicate, then we could have: switch(e) { and similarly for catch statements: catch(e) { |
This comment was originally written by @tomochikahara I think Enum pattern matching in Haxe is easy to add to Dart with enum. enum Card { number(num n), jack, queen, king, joker } num toNumber(Card card) => switch(card) { |
Removed this from the Later milestone. |
Removed Oldschool-Milestone-Later label. |
Issue dart-lang/sdk#21847 has been merged into this issue. |
Is matcher ready for daily use at regular code? Or it should be placed only at tests? |
The matcher class is definitely well tested, but it's built for testing, not performance. I don't think it'll make a good substitute for language-level pattern matching. |
That could be more beautiful switch (value) {
on Exception: throw value;
on const RegExp(@"\s+") return 'whitespace';
on 42: return 'the answer';
otherwise: return 'unrecognized';
} And to make it possible you can use Unification* |
Why not pattern matching in Haskell/Erlang Style, on the function arguments ? |
I like the Java or Scala style. |
@hepin1989 , so you prefer to solve, let's say the problem n. 1 (of the 99 problems like: <T> T f1(List<T> list) {
if (list.isEmpty()) throw new NoSuchElementException("List is empty");
List<T> elements = list.tail();
List<T> result = list;
while (elements.nonEmpty()) {
result = elements;
elements = elements.tail();
}
return result.head();
} instead of: f1 :: [a] -> a
f1 [x] = x
f1 (_:xs) = f1 xs :) |
@leafpetersen @munificent @lrhn – should this be in the language repo? |
#1047 (comment) @henry-hz your first example is too long and your second is too hard to read, it's too different from what most people know. Typing less caracter doesn't make it easier. What about the Kotlin style ? fun f1(list: List): List = when(list) {
list.isEmpty() -> throw Stuff
list.tail().isEmpty() -> list.head()
else -> f1(list.tail())
} (it may contain errors, i'm on mobile and don't do Kotlin often) You have to type more than your second example, but i think any programmer can understand this piece of code, unlike yours. |
I would prefer the C# pattern matching style. interface IVehicle {}
class Car : IVehicle {}
class Motorcycle : IVehicle {}
IVehicle myVehicle = getVehicle();
if (myVehicle is Car car)
{
// car is available as a Car in this scope
}
else if (myVehicle is Motorcycle motorcycle)
{
// motorcycle is available as a Motorcycle in this scope
}
// Inverse usage
if (!(myVehicle is Car car))
{
// car is not available in this scope
}
else
{
// car is available as a Car in this scope
} |
We do have pattern matching enabled in the bleeding edge SDK, but it isn't user extensible in the sense that, say, F# active patterns are. We don't have patterns for RegExp matching, and the semantics aren't extensible to let you do that in a library, so I'm going to leave this open. I am interested in extending pattern matching to support user defined matching behavior, but I'm not sure what kind of priority it will have. |
Please don't, but: extension REMatch on String {
bool operator <(Pattern pattern) => pattern.allMatches(this).isNotEmpty;
bool operator >(String regexp) => RegExp(regexp, multiLine: false).hasMatch(this);
bool operator >=(String regexp) => RegExp(regexp, multiLine: true).hasMatch(this);
}
void main() {
switch ("abc") {
case >= r"^[abc]*$": print("Contains only 'a', 'b' and 'c's");
case < "ab": print("Contains 'ab'");
}
} All you need is matched value type with no built-in Not sure whether that suggests a way to extend patterns, or it suggest which way not to go :) It's also only a true/false match, which doesn't allow you to inspect the match itself. If we allowed object pattern getters to be general selectors (which we should, #2433), and constant extension StringFirstMatch on String {
Match? firstMatch(Pattern pattern) => pattern.firstMatch(this);
}
switch ("abc") {
case String(firstMatch(const RegExp(r"(?:(abba)|[abc])*$")): Match([0]: var all, [1]: var abba?)?):
print("All a's, b's and 'c's, and at least one 'abba'.");
... You can do this today as well, you just need to write an extension per regexp: extension AbbaMatch on String {
static final _abbaRE = RegExp(r"(?:(abba)|[abc])*$");
RegExpMatch? get abbaMatch => _abbaRE.firstMatch(this);
}
extension MatchCaptures on Match {
String get $0 => this[0]!;
String? get $1 => _tryGroup(1);
String? get $2 => _tryGroup(2);
String? get $3 => _tryGroup(3);
String? get $4 => _tryGroup(4);
String? get $5 => _tryGroup(5);
String? get $6 => _tryGroup(6);
String? _tryGroup(int i) => this.groupCount >= i ? this[i] : null;
}
// ...
switch ("abc") {
case String(abbaMatch: RegExpMatch($0: var all, $1: var abba?)?):
print("All a's, b's and 'c's, and at least one 'abba'.");
... |
Could we repurpose let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|ParseRegex|_|) regex str =
let m = Regex(regex).Match(str)
if m.Success
then Some (List.tail [ for x in m.Groups -> x.Value ])
else None
let parseDate str =
match str with
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{1,2})$" [Integer m; Integer d; Integer y]
-> new System.DateTime(y + 2000, m, d)
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{3,4})" [Integer m; Integer d; Integer y]
-> new System.DateTime(y, m, d)
| ParseRegex "(\d{1,4})-(\d{1,2})-(\d{1,2})" [Integer y; Integer m; Integer d]
-> new System.DateTime(y, m, d)
| _ -> new System.DateTime() Could look like this in Dart: typedef Integer = int? Function(String input) => int.tryParse(input);
typedef ParseRegex = List<String?>? Function(String regex, String input) {
final exp = RegExp(regex);
final match = exp.firstMatch(input);
return match?.groups(List.generate(match!.groupCount -1 , (index) => index + 1));
}
DateTime parseDate(String date) => switch(date){
ParseRegex(r'(\d{1,2})/(\d{1,2})/(\d{1,2})$', [Integer(m), Integer(d), Integer(y)]) => DateTime(y + 2000, m, d),
ParseRegex(r'(\d{1,2})/(\d{1,2})/(\d{3,4})', [Integer(m), Integer(d), Integer(y)]) => DateTime(y, m, d),
ParseRegex(r'(\d{1,4})-(\d{1,2})-(\d{1,2})', [Integer(y), Integer(m), Integer(d)]) => DateTime(y, m, d),
_ => DateTime.now()
}; The last argument would be where the value being tested would be applied and where the pattern would be checked. |
This issue was originally filed by [email protected]
It would be nice to match a value against patterns with a terse syntax.
This is similar to 'switch' (and could possibly share the same syntax) but instead of exact-match, the semantics would be determined by the pattern used.
When the pattern is a RegExp, it would match strings that conform to it.
A Type would match instances of that type.
User classes should be able to implement their own pattern behaviour.
Any value without special pattern behaviour would match using ==
for example:
There was some discussion here: https://groups.google.com/a/dartlang.org/group/misc/browse_thread/thread/a3e75c24c6dd4f03/
A pattern might also be able provide values which can be bound to names (e.g. the Match object corresponding to a regexp match).
The text was updated successfully, but these errors were encountered: