In my own tests, F# will require about 1/20th of the type annotations of C#. It offers a near superset of functionality, so you don't lose anything (except a bit on the tooling side of things).
The lightweight syntax, easy handling of functional features, nesting of code - it ends up leaving you with far less code that has less bugs by default. MS describes F# as excelling at "programming in the small" - the line-by-line, char-by-char code is simply far superior to using C#. Just type inference alone is huge - C# is embarrassingly bad at type inference, and it's partially due to the complexity in their codebase in implementing it properly. At the moment, MS seems to have sort of backed off on enhancing C#.
There's so many tiny things in F# that just make the code easier and less bug prone. For instance, being able to nest code. Suppose I want to read an integer out of a querystring, and on failure provide a default. Idiomatically, in C#:
int age;
if (!int.TryParse(qs["age"], out age)) age = -1;
In F#:
let age = try int qs.["age"] with _ -> -1
Or lets say I have some intermediate variables. In C#:
int age;
var parts = String.Split(bla, ",");
if (parts.Length == 0) age = -1;
else age = int.Parse(parts[0]);
I've introduced a useless local var, parts, for no reason other than needing it in computing age. In F# (and avoiding pattern matching just to demonstrate):
let age =
let parts = String.Split(bla, ",")
if parts.Length = 0 then -1 else int parts.[0]
The intermediate binding, is not available outside it's scope. That's far cleaner, and quite commonly useful.
Top level functions are handy. Being able to make useful lambdas without specifying the types is super useful. Many of my functions have a nested helper function which makes things cleaner. Places where I don't want to add a full utility function, but I don't want to repeat code. You can do it in C#, but it's ugly. In F# it's natural.
Array/list comprehensions. In C#:
var res = new List();
using(var reader = cmd.ExecuteReader()) {
while(reader.Read()){
res.Add(reader.GetInt32(0));
}
}
In F#:
use reader = cmd.ExecuteReader()
let res = [ while reader.Read() do yield reader.GetInt32(0) ]
Pattern matching is well covered, and F# Active Patterns are just awesome, because you can customize your own destructuring for arbitrary objects. The list just goes on and on, lots of things in F# that are just so right, so easy, so trivial. Custom operators, tuple handling.
I'm not even touching on the larger things, like Type Providers, records, sum types, or the fact that F# has monad syntax that's flexible, instead of C# building specific monads into the language. Heck, the F# compiler is far more advanced and produces faster code in many cases. And the fact that you can manually make it inline code is also a nice perf win you simply cannot get with C#.
I recently started working on a project that uses C#, and it's just such a pain. Death by a thousand cuts. There's no reason to use C# over F#. Even if you only use F# as a light-syntax-C#, you're better off.
The only real objection is hiring, in an enterprise scenario. For any serious project where the code is the product (versus the code being an artifact of just solving a business problem), I don't believe you can hire any good programmer that cannot deal with F#. (I think that hold for most languages, in general.) Unfortunately, people don't seem to believe that, and somehow think they're going to hire really smart people, but these people aren't going to be able to learn a different language.