4 ms·
Fully agreed on pattern matching not being an expression. I really dislike that syntax overall. I get the reluctance to introduce new keywords (though this did
by moogly 10y ago
Fully agreed on pattern matching not being an expression. I really dislike that syntax overall. I get the reluctance to introduce new keywords (though this did introduce 'when', but I suppose it's contexual at least), but trying to shoehorn pattern matching into the, um, let's be nice and call it "venerable", old switch/case does not look very ergonomic at all.
And I don't know about you, but I rarely switch over polymorphic types these days, I mostly switch over values, so the pattern matching overall is way less useful to me without records/discriminated unions.
I guess you can't really remove 'ref' and 'out' due to interop. Maybe the ref returns stuff will be nice if we get more performant string/array views in the next version, and I do like the inline 'var out'.
I also want type inference for field declaration+initialization, but I think Eric Lippert once explained why that was a difficult problem to solve, albeit I can't remember the reason.
- louthy 10y agoI think switch could have been made into an expression very easily by following the lead of lambdas and method-body expressions, namely the => arrow. var area = switch ( case Line l => 0; case Rectangle r => r.Width * r.Height; case Circle c => Math.PI * c.Radius * c.Radius; default: throw new ApplicationException(); ) That 'feels' to me to be consistent with the expression approach taken elsewhere, whilst making it clear to anybody used to seeing the statement based switch that it's different. > so the pattern matching overall is way less useful to me without records/discriminated unions. I agree partially. I think it's possible (I'll investigate tomorrow) to provide an 'is' operator override. So for example in my implementation of Option<T> [1] (which would usually be a sum-type of Some(x) and None, I instead use a struct (to remove a 3rd state of null). With the 'is' operator I'm hoping to do this: public static class Some { public static bool operator is(Option<T> option, out T value) { value = option.Value; // Value is internal return option.IsSome; } } switch(option) { case Some x: ...; break; default: ...; break; } I'm not sure if I can do this: public static class None { public static bool operator is(Option<T> option) { return option.IsNone; } } switch(option) { case Some x: ...; break; case None: ...; break; } So although it doesn't solve the general need for sum-types and record-types, it should hopefully facilitate some of the common use cases. Agreed about ref and out; I understand their purpose, I just despise them with a passion. Their whole syntax and ickyness makes me feel queesy. The inline var out feels like an imbalanced expression. They're clearly more useful now, but they still feel odd. [1] https://github.com/louthy/language-ext/blob/master/LanguageExt.Core/Option.cs#L24 https://github.com/louthy/language-ext/blob/master/LanguageE...
- Locke1689 10y agoRegarding switch-as-expression -- the semantics of pattern matching as an expression can be subtle, especially when paired with nullability, so I think there's more support for introducing a new "match" expression that does what you describe. Switch is more of an effort to integrate patterns in the way C# is used right now.
- louthy 10y ago> the semantics of pattern matching as an expression can be subtle, especially when paired with nullability Could you expand upon that? If I can do this: var area = shape is Line l ? 0 : shape is Rectangle r ? r.Width * r.Height : shape is Circle c ? Math.PI * c.Radius * c.Radius : throw new ApplicationException(); How is this, a collection of predicates and expressions, not the same? var area = switch(shape) { case Line l => 0; case Rectangle r => r.Width * r.Height; case Circle c => Math.PI * c.Radius * c.Radius; default => throw new ApplicationException(); } I realise you work on Roslyn (and you said semantics, so my example may be a bit off), so I'd love the detail on why the above is problematic.
- Locke1689 10y agoWell, let's say Line, Rectangle, and Circle are reference types. Which case does null match? If you match the first type, that's somewhat unsatisfying. But if you only match with default, that's also unsatisfying because now we would have a problem with completeness -- if the match doesn't succeed then you have a potentially unassigned variable. So now every match expression would have to have a default case just to handle null, but you also don't gain the advantages of static matching because everything matches default, so if you do legitimately forget a case you get no warning. Existing switch statements are much more resilient to these matters simply because they aren't expected to be exhaustive right now. People are used to the fact that they have to deal with unassigned variables or completeness failures. The match expression, however, I want to be more like ML where you can get strong guarantees on the "irrefutability" of a match.