I've been looking at F# recently, and while I'm not likely to leap the fence any time soon, it definitely highlights some areas where C# (or library support) could make life easier.
In particular, I'm thinking about the pattern matching capability of F#, which allows a very rich syntax - much more expressive than the current switch/conditional C# equivalents. I won't try to give a direct example (my F# isn't up to it), but in short it allows:
- match by type (with full-coverage checking for discriminated unions) [note this also infers the type for the bound variable, giving member access etc]
- match by predicate
- combinations of the above (and possibly some other scenarios I'm not aware of)
While it would be lovely for C# to eventually borrow [ahem] some of this richness, in the interim I've been looking at what can be done at runtime - for example, it is fairly easy to knock together some objects to allow:
var getRentPrice = new Switch<Vehicle, int>()
.Case<Motorcycle>(bike => 100 + bike.Cylinders * 10) // "bike" here is typed as Motorcycle
.Case<Bicycle>(30) // returns a constant
.Case<Car>(car => car.EngineType == EngineType.Diesel, car => 220 + car.Doors * 20)
.Case<Car>(car => car.EngineType == EngineType.Gasoline, car => 200 + car.Doors * 20)
.ElseThrow(); // or could use a Default(...) terminator
where getRentPrice is a Func<Vehicle,int>.
[note - maybe Switch/Case here is the wrong terms... but it shows the idea]
To me, this is a lot clearer than the equivalent using repeated if/else, or a composite ternary conditional (which gets very messy for non-trivial expressions - brackets galore). It also avoids a lot of casting, and allows for simple extension (either directly or via extension methods) to more-specific matches, for example an InRange(...) match comparable to the VB Select...Case "x To y" usage.
I'm just trying to gauge if people think there is much benefit from constructs like the above (in the absence of language support)?
Note additionally that I've been playing with 3 variants of the above:
- a Func<TSource,TValue> version for evaluation - comparable to composite ternary conditional statements
- an Action<TSource> version - comparable to if/else if/else if/else if/else
- an Expression<Func<TSource,TValue>> version - as the first, but usable by arbitrary LINQ providers
Additionally, using the Expression-based version enables Expression-tree re-writing, essentially inlining all the branches into a single composite conditional Expression, rather than using repeated invocation. I haven't checked recently, but in some early Entity Framework builds I seem to recall this being necessary, as it didn't like InvocationExpression very much. It also allows more efficient usage with LINQ-to-Objects, since it avoids repeated delegate invocations - tests show a match like the above (using the Expression form) performing at the same speed [marginally quicker, in fact] compared to the equivalent C# composite conditional statement. For completeness, the Func<...> based-version took 4 times as long as the C# conditional statement, but is still very quick and is unlikely to be a major bottleneck in most use-cases.
I welcome any thoughts / input / critique / etc on the above (or on the possibilities of richer C# language support... here's hoping ;-p).