I like F#'s syntax when all you're doing is pure logic. But when you have to interface with any IO like a database or REST call or something, you have to abandon the elegance of ML syntax and use these ugly computation blocks. In C# you can do something like this:
var post = await _postService.getById(id);
in F# the equivalent is basically
let getPostById id = async {
let! post = blogPostService.getPostById id
return post
}
let post = getPostById 42 |> Async.RunSynchronously
But not really, because RunSynchronously isn't the same thing as `await`. Realistically if you wanted to handle the result of an async computation you would need to create continuations. F# isn't the only ML-family language to suffer from this; Ocaml does as well. It always seemed to me like the pattern with any asynchronous operations in F# is to either:
1. Do all logic in ML-syntax, then pass data into a computation block and handle I/O operations as the last part of your function, then return unit OR
2. Return a C#-style Task<> and handle all I/O in C#
Either way, ML-style languages don't seem like they're designed for the kind of commercial CRUD-style applications that 90% of us find ourselves paid to do.
1. Do all logic in ML-syntax, then pass data into a computation block and handle I/O operations as the last part of your function, then return unit OR
2. Return a C#-style Task<> and handle all I/O in C#
Either way, ML-style languages don't seem like they're designed for the kind of commercial CRUD-style applications that 90% of us find ourselves paid to do.