Yes. Your standard OOP features like inheriting from C# classes, implementing interfaces, etc are supported natively in F#. The main hassle in being C#-compatible is exposing an external OOP api with only .NET types and interfaces instead of standard F# types. This means, for example, exposing `seq` (`IEnumerable`) instead of `list` (`Microsoft.FSharp.Collections.List`) or using the struct-tuple construct in F# 4.1 so C# 7 can access it.
> good samples of real life application code
No personal recommendations but have you seen https://github.com/fsprojects/awesome-fsharp
Well, yes and no. You definitely can do this, and for some applications it's worth it, but often the overhead at the API level will make you cringe.
APIs designed for C# are very different than APIs designed for F#, to account for partial application of functions, sum-types, non-nullability, immutability and value-like behavior by default from F# types, and also to make good use of F#'s type inference. Also, given stuff like the |> operator, making "fluent" APIs looks very different.
Using APIs made explictly for F# from the side of C# is somewhere between enormously annoying and de-facto impossible. The other way 'round, it's just massively annoying, as you lose many advantages of F#, and the code will not be much more compact as if you would write it from C#.
Pain-points in practice are around F# functions vs. C# methods and delegates, async/await vs. async-expressions, code quotations vs. Linq expressions, etc.
To be productive in F#, you want your API small and primitive, and stay within the language for the rest. If this is possible, go for it; if not, evaluate.
A final tip: Porting a big block from C# to F# is much less work than it sounds, and opens the door to refactoring that part into something sensible.
If there were a ton of F# libraries that showed how to create functional APIs people could learn from them by using them the same way a lot people learn OOP by using APIs.
As it is now the only way to get into F# is to have a group who really wants to do it and learn. Otherwise it's just an uphill battle.
Starting with front-end since it's a bit easier to jump into
Back-End C#, front-end SPA in F#
Check out Fable-Elmish (and Fable-Elmish with React (Elm-style model-update-view for React)) https://github.com/fable-elmish/elmish
WPF Desktop -> Elmish.WPF (Elm-style model-update-view for WPF) Check-out https://github.com/Prolucid/Elmish.WPF
Back-End Slices for lambda-architecture-style systems
Web,Front-end in whatever, streaming with Storm and F# via FsShelter (and FsCassy for Cassandra, FsBunny for RabbitMQ)
Streaming, front-end in whatever, Web Layer with Freya
Also: Some tips Install VS Code with all the ionide plugins. Also grab Forge to create new fsharp projects if you're not doing net core development. Use Expecto for tests. Then from the editor you can build,run, debug, paket install, and run test(s)
At my company we have a C# codebase for a large web application, but we create a 2 stage caching feature for it in F#. This F# project uses a couple of C# oriented libraries (stackexchange.redis and protobuf-net). So lots of interop going both ways.
Some examples of people using it in production: http://fsharp.org/testimonials/
All of the "real" code in that app is in an F# library, which you add to the Visual Studio solution just like you would any other C# helper library. When you add a source file to that library, it's an F# source file, and it's built with the F# compiler into the library, which is then linked with the C# "main" program.
There are a few tricky parts, owing to the substantial differences between C# and F#. They all come down to the fact that there are aspects of both C# and F# that don't map naturally to the other. While there may be low-level .NET or IL ways to force the call, it's better to restrict your use of each language at the boundary between the two parts of the program.
Some examples:
1. F# has two ways of passing parameters to a function called the tuple and curry forms. Curried functions are the full-power form, but C# can't call them directly, so any function or method you want to export to the C# side must use the tuple form. At a superficial level, the only difference is some parentheses and commas, but at a low level, you're purposely nerfing the F# function to allow C# to call it. This is not an area you can avoid knowing about, since all of .NET uses tupled calling form, being written for C# initially.
2. F# has several native data types that have no direct implementation in C#. Some newer versions of C# have .NET extensions to allow them to manipulate values of these types given to the C# side from the F# side, but given a choice, it's often simpler to restrict the set of data types you define the interface between the two in terms of. That is, you generally don't want to allow discriminated unions, records, tuples, or function values through the boundary, if you can help it. Use data types C# copes with better: classes, structures, lists, and arrays instead.
3. C# code often makes use of null values, which are...well, "sinful" in F#. It is possible to use null in F#, but strongly recommended against, since F# is all about definite value states, and null adds an element of indefinite-ness to the application. You can choose either to allow null values through the C# to F# boundary or enforce the F# side's wish not to accept them at the boundary. Your choice.
4. Once you get into the F# way of doing things, you start to become more choosy about the C# libraries you use from F#, since some are more acceptable from an F# standpoint than others. You start to avoid libraries that have mutable values flying around everywhere, methods with side effects, etc. Some C# libraries are outright annoying to use, because you have to continually cast away so much of the F# value in order to use them. Fortunately, .NET itself is generally pretty good in this regard. The NuGet world shows less restraint here.
And there is the famous line "Functional programming? We did functions 30 years ago. No big deal".
I was sole developer on that project.
But here's a thought for you: you can only achieve mediocrity if all choices require 100% buy-in from all interested parties.
> I have found most people simply don't care and don't want to learn it.
No one likes to have their cheese moved. But cheese moves nevertheless.
> We did functions 30 years ago.
Most people saying that probably don't realize that what computer programmers call "functions" are not what mathematicians call "functions."
The reason FP is becoming popular now is that the distinction now matters much more than ever in today's multicore world, where multiprocessing is often the only practical way to make software substantially faster.
Exactly! Unfortunately this is the reality in a lot of corporations. Pretty sad.