But that struct example is less than great.
Just last night I had a readonly C# struct like that, that I used as a dictionary key and wondered why my perf tests took a nose dive when the Dictionary hit a few dozen entries.
My boneheaded mistake was i forgot to override GetHashCode and Object.Equals. Great my perf was looking decent, but even then I was allocating 32 bytes of memory on every lookup.
I forgot I needed to also implement IEquatable<> to prevent boxing on calls to Object.Equals.
My little 2 item struct is 3 lines of definition and 7 lines of boiler plate to get it right. and my GetHashCode impl is suboptimal.
F# records do all of that for you. every time. automatically.
but saying "unless someone knows C# very well one could have easily gotten this wrong" is disingenuous at best.
One thing that makes it a bit nicer in F# than in C# is that you can define object expressions wherever an interface is expected, so there's no need to declare a new type in the static scope somewhere (similar is possible in Java).
DoSomething { new ISortAlgorithm with
member this.Sort values = ... }
values
In terms of how they work, there's not really much difference. If something expects a function and you need to capture some local variable within that function, the framework will create a new type for that closure anyway. delegate List<int> SortAlgorithm(List<int> values);
List<int> QuickSort(List<int> values) =>
values; // Do QuickSort
List<int> MergeSort(List<int> values) =>
values; // Do QuickSort
void DoSomething(SortAlgorithm sort, List<int> values)
{
var sorted = sort(values);
}
public void Main()
{
var values = new List<int> { 9, 1, 5, 7 };
DoSomething(QuickSort, values);
}The delegate obviously breaks down when you have more than one method. For that interfaces work just fine. Even in the case of using a delegate where you capture variables, the C# compiler creates a new type, instantiates it and passes the method in place of the delegate. It's hardly any different to deriving from an interface.
I just tend to use interfaces more out of habit even for single methods. Creating an object expression in F# is pretty much the same outcome as creating a closure in C#.
EDIT: I guess the other problem is that F# functions are not directly compatible with Func<_> and Action<_> types, so when calling a C# function expecting these, you need to wrap the call in an anonymous function. C# interfaces and F# classes are compatible though.
The code in the article isn't outlandish or particularly verbose.
This is why most C# developers would use an interface for this. With an interface you add extra parameters or dependencies to the constructor of the concrete implementation. If this example wasn't sort algorithms but password hashing algorithms this would be clear.
So yeah.. it proves my point... writing good C# is not that easy, requires a lot of experience and mistakes before someone knows how to write SOLID code in C#. Thanks for helping my point!
void List<int> Sort(List<int> input, bool someParam){}
var values = new List<int> { 9, 1, 5, 7 };
DoSomething(x => Sort(x, true), values);
Thanks for proving my point that you really don't know C#.Because your F# example is the same except it's trading Func<List<int>, List<int> for (int list -> int list)
Func<T[], T[]> where T : IComparable<T>
But maybe all of that is just stronger arguments for F#'s inferred typing...
Take the "To hell with interfaces" example.
No, no, no, no, nope. No interface necessary.Or immutability:
So far so good, but unless someone knows C# very well one could have easily gotten this wrong."Really?? This is beginner stuff.
I 100% agree that null sucks, functions should not need to be in classes and immutability is great, but this kind of strawman doesn't help his point.