I think you need do-notation for true simplicity. This is because handling results, optionals etc without this is really hard to read, maybe more so than the imperative code equivalent. I hope TypeScript gets this but not holding my breath!
f1 = do
fd <- openFile "/file/path"
contents <- readFile fd
let result = process contents
closeFile fd
return result
f2 = openFile "/file/path" >>= \fd ->
readFile_ fd >>= \contents ->
let result = process contents in
closeFile fd >>
return result
Sure, the code for f1 is easier to read, but not by that much. You have a bit more noise in each line with the explicit operators being added. The only big readability difference I see is that you have moved to bound variable to the end of lines instead of the start. I've never actually worked in a codebase that regularly did this, but I suspect you would get used to it frequently. The inconsitency with let statements putting the bound variable on the left would be a readability drawback; but I can easily imagine a language that allows for putting the bound variable on the right hand side of an assignment, such as (not Haskell): let process contents -> result in