I am trying to design a library in F#. The library should be friendly for use from both F# and C#.
And this is where I\'m stuck a little bit. I can make it
You only have to wrap function values (partially-applied functions, etc) with Func or Action, the rest are converted automatically. For example:
type A(arg) =
member x.Invoke(f: Func<_,_>) = f.Invoke(arg)
let a = A(1)
a.Invoke(fun i -> i + 1)
So it makes sense to use Func/Action where applicable. Does this eliminate your concerns? I think your proposed solutions are overly-complicated. You can write your entire library in F# and use it pain-free from F# and C# (I do it all the time).
Also, F# is more flexible than C# in terms of interoperability so it's generally best to follow traditional .NET style when this is a concern.
EDIT
The work required to make two public interfaces in separate namespaces, I think, is only warranted when they are complementary or the F# functionality is not usable from C# (such as inlined functions, which depend on F#-specific metadata).
Taking your points in turn:
Module + let bindings and constructor-less type + static members appear exactly the same in C#, so go with modules if you can. You can use CompiledNameAttribute to give members C#-friendly names.
I may be wrong, but my guess is that the Component Guidelines were written prior to System.Tuple being added to the framework. (In earlier versions F# defined it's own tuple type.) It's since become more acceptable to use Tuple in a public interface for trivial types.
This is where I think you have do things the C# way because F# plays well with Task but C# doesn't play well with Async. You can use async internally then call Async.StartAsTask before returning from a public method.
Embrace of null may be the single biggest drawback when developing an API for use from C#. In the past, I tried all kinds of tricks to avoid considering null in internal F# code but, in the end, it was best to mark types with public constructors with [ and check args for null. It's no worse than C# in this respect.
Discriminated unions are generally compiled to class hierarchies but always have a relatively friendly representation in C#. I would say, mark them with [ and use them.
Curried functions produce function values, which shouldn't be used.
I found it was better to embrace null than to depend on it being caught at the public interface and ignore it internally. YMMV.
It makes a lot of sense to use list/map/set internally. They can all be exposed through the public interface as IEnumerable<_>. Also, seq, dict, and Seq.readonly are frequently useful.
See #6.
Which strategy you take depends on the type and size of your library but, in my experience, finding the sweet spot between F# and C# required less work—in the long run—than creating separate APIs.