I wrote up how I'd do this in scala, just to compare and contrast
val shape: Option[Shape] = ???
shape.map {
case Square(0) | Circle(0) => 0
case Triangle(b, h) if b == 0 || h == 0 => 0
case Rectangle(l, h) if l == 0 || h == 0 => 0
case Square(side) => side * side
case Circle(radius) => radius * radius * math.Pi
case Triangle(base, height) => base * height / 2
case Rectangle(length, height) => length * height
}
what I noticed
* scala doesn't have a way for cases to fall through, so the first cases have to each declare that the result is 0. it would be cool if we could use something in place of '=>' to make the case fall through.
* C# doesn't have structural matching, so the 'when' keyword is used more often.
* scala's pattern matching is exhaustive so if we assume the shapes are in a sealed type hierarchy then we don't need a default case.
* it's idiomatic to use 'Option' instead of null in scala, but there are lots of libraries in c# that offer option monads, so it's more a point of what's idiomatic than what's possible.
just FYI, the C# can be rewritten as a switch expression which is more comparable to the scala
return shape switch
{
Square s when s.Side == 0 => 0,
Circle c when c.Radius == 0 => 0,
Triangle t when t.Base == 0 || t.Height == 0 => 0,
Rectangle r when r.Length == 0 || r.Height == 0 => 0,
Square s => (s.Side * s.Side),
Circle c => (c.Radius * c.Radius * Math.PI),
Triangle t => (t.Base * t.Height / 2),
Rectangle r => (r.Length * r.Height),
_ => throw new ArgumentException(message: "shape is not a recognized shape", paramName: nameof(shape))
};
C# 8 also has nullable reference types, i.e. comprehensive nullability analysis, which is comparable though not identical to an option type. I really wish for exhaustive pattern matching and ADTs in C#, though.
It always feels a bit half baked when the class library isn’t designed with it though. It would be as if generics were added but not System.Collections.Generic. So while I long thought it would be the perfect addition to C#, I’m not so enthusiastic any more. It would be great (F# is!) but not as good as it would be in a language and platform where it was there all along.
In F# this is already an issue when you want to use Option/Result but as soon as you do anything with the BCL you need to handle exceptions instead, and convert to/from error cases of ADTs.
Nullable reference types also feel pretty half baked with EF at the moment. If you have a non-nullable column that you want to represent with a non-nullable property in your EF model, pretty much the only option (the last time I looked) was to configure EF to use a nullable private field and access it through a non-nullable public property. It's a lot of extra work.
>We will also aim to be done with null-annotating our core libraries when .NET 5 (November 2020) comes around – and we are currently on track to do so. [1] [2]
> It would be as if generics were added but not System.Collections.Generic
I started with C# in the 1.0 days before generics. The lack of strongly typed collections was very annoying with all of the casts involved. I used to use a template engine/code generator to create strongly typed collections. Can't remember the name of the tool... Probably been since 2002 or 2003 since I used it.
I’m surprised there isn’t an F# feature that automatically wraps a call with a try/catch wrapper that returns an Option<T,TException> - something like that could be done in a library, right?