> The downside is then you have to know how to write wrappers/helpers for them and probably don't get to use all the cool stuff that comes with F# because you're using a library written for C#.
For any long term project you should own your abstraction anyway, i.e. a wrapper/interface. Not only do you need it to properly test it, you never know when that 1 hour spend creating your abstraction, gives you back in ease when an external dependency inevitably needs to be replaced.
I think there's a fundamental mistake in architecting around pluggable dependencies. I've never worked in a codebase that was improved by treating a dependency as a thing that could be replaced at any time, in fact it only made me question why dependencies were expected to change so often. Maybe you want to spend more time prototyping if even the basic foundation of your software is so volatile.
You know? It's not the same as keeping your data layer and HTTP layer separate, or your local state separate from your rendering layer, and encapsulating business logic. Nor is it the same as building a modular system designed around your business's domains, so you can change or replace those as the needs of the business shift over time.
For example, we encapsulate our email sending service (Sendgrid). Sendgrid has a pretty good client library, so other than providing a slightly more idiomatic F# API, our wrapper layer doesn't do much.
However, (1) sending email is a very straightforward service, so if Sendgrid ever becomes expensive, unreliable, or otherwise limited, replacing it with a different service is quite realistic. (As opposed to e.g. a database or caching layer where you would probably have to also review all the _logic_ if you switch to a different engine). And (2), we don't want to send real emails during tests (especially e.g. fuzzy or property which run en masse), so you actually want a honest-to-god mock here.
It's not so much that you expect them to change, but when developing with unittests it's a necessity. And it makes it possible to have the right degree of in memory component and integration testing. It forces you to think about the interface with that dependency, and ask the right questions instead of potentially using some other domains abstraction a lot. It's not different than thinking in modular design.
The added benefit is that when, not if, you have to replace some dependency it's simple.
For any long term project you should own your abstraction anyway, i.e. a wrapper/interface. Not only do you need it to properly test it, you never know when that 1 hour spend creating your abstraction, gives you back in ease when an external dependency inevitably needs to be replaced.