It's not any lack on your part. F# doesn't really support the
really helpful abstraction techniques like parameterised modules or typeclasses. You can roll your own typeclasses using just simple records containing functions. It's easy, idiomatic, and it works statically. E.g., imagine you have a users 'service', with operations 'get by ID', 'add', and 'rename':
(** Type-safe IDs using a phantom type. *)
module Id =
type 'a t = private T of uint64
let of_uint64 u = T u
let to_uint64 (T u) = u
(** A domain type. *)
module User =
type t = private { id : t Id.t; name : string; age : int }
let make uid name age : t = ...
...
(** Users service typeclass. *)
module User_service =
type t =
{ get_by_id : User.t Id.t -> User.t Async
add : User.t -> unit Async
rename : string -> User.t Id.t -> unit Async }
let db : t =
{ get_by_id = fun uid -> ...
add = fun u -> ...
rename = fun name uid -> ... }
let test : t =
Now, injecting a user service dependency into any function is equivalent to passing in a parameter of type `User_service.t`.
As for free monads, I don't think F# will make them easy. If you notice, one commenter in that GitHub discussion mentioned they were doing a lot of copy-pasting to implement FMs. Imho that's a bad sign.