Well there are plenty of explanations of C#'s basic syntax, classes, structs, etc., but nothing that specifically addresses the functional mindset a Haskell programmer would be starting with, so I wrote a reply providing links to the various familiar concepts from functional programming found in C#, and described various caveats that might be surprising to a Haskell user. I'll reproduce the post here for posterity:
I think there's a reasonable C# subset for functional programming, so if you stick to that you should be able to pick it up relatively quickly. Read up on:
- LINQ -- you can use the query comprehension syntax, or the regular first-class function syntax. The former is sugar for the latter.
- Lambdas and delegates (delegates are first-class functions)
- Parametric polymorphism is known as generics. You can place generic parameters on methods/functions, and type declarations. The standard System.Func* and System.Action* delegates. These are delegates with generic parameters. Most first-class functions you deal with will be one of these two classes of delegates.
- Type constraints
- lambdas and delegates are nominally typed, so you can't implicitly or explicitly coerce a delegate of one type into a delegate of a compatible signature, ie. System.Predicate<int> is signature compatible with System.Func<int, bool>, but they are not interconvertible without doing some magic like I do in my Sasa.Func.Coerce library function.
- methods and delegates are multiparameter, not curried like in OCaml and Haskell, thus leading to all the Func* and Action* overloads.
- The "void" return type is not a type, so it can't be used as a generic argument. Hence the need for all the Action* delegate types distinct from the Func* delegate types. Action* differ only in the fact that they return void.
- generic parameters on methods are strictly more general than generic parameters on delegates (which are types). Method generics support first-class polymorphism, while type declaration generics do not.
- classes are always implicitly option types, ie. they are nullable, while struct types always have a "valid" value, ie. are not nullable. Struct types are then useful for eliminating null reference exceptions in programs, as long the default struct value is meaningful.