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 inFirst class functions are definitely a must.
I prefer working in statically typed languages, but I agree with you (though only if we use a colloquial definition of "sound").
And, what's more, I think the correspondent in the article would agree also. Michael Sperber is very familiar with Scheme, as evidenced by his publication history. I think your point actually connects with a line from the article:
> Components in functional programming are essentially just data types and functions, and these functions work without mutable state, [Sperber] said.
Even in a dynamically typed language, the data still "has a type" — but now we're talking about the "shape of the data" rather than a formal type system. Reasoning about data-shapes is perfectly valid, and still supports the style of code that Sperber talks about throughout the interview.
This also connects to the "Design Recipe" from the textbook How to Design Programs, which is an introductory CS text based on Racket, a derivative of Scheme. In the Design Recipe, students are told that they should write down the contract of their function before implementing the body of the function, and part of this is the specification of the "types" of the inputs and the output. Again, since the language is untyped, this has much more to do with conveying a sense of the "shape" of the data to the programmer, but it is still highly effective in guiding the design and implementation of a functioning software system.
If you look at a language like Haskell, types are there to provide polymorphism and allow you to rearrange the language in convenient ways. It's impossible to say what is the single main use of types there, but this is certainly one of the most important ones. Another main use of types there is documenting interfaces.
People only get to say that types provide nothing else but correction if they decide that all the other uses are non-important and should be ignored. But then, that's a pretty trivial and useless information.
I believe Mike Sperber is talking about Haskell when he says "advanced abstraction", but most regular line-of-business software can reap significant benefits with just simple FP principles: mostly immutable data, record types, sum types, and a vocabulary of small decomposed functions that can parse, transform, and combine this data, together building to larger wholes.
You do need a language with types and first-class functions, and the most common vehicle for that currently is TypeScript. Combine that with a book like Grokking Simplicity by Eric Normand, especially the fundamental idea of separating data, computation, and action, and you have a software system that is supple, simple, and amenable to continuous growth and refactoring over a long period of time.
Grokking Simplicity, IMO, is a severely under-discussed book when it comes to FP. Its only sticking point for me is its lack of static types, but otherwise it is the first and only book that I've seen that distils the functional way of thinking for the working programmer in an accessible way, without having to resort to the all-or-nothing proposition of completely pure FP.