I think a part of it is that you're restricting the discussion to examples too small to be meaningful.
Does the fact that IO forms a monad help us, when all we're doing is sequencing actions in a static order without interesting interactions? No, not really. It also doesn't hurt.
main = do
let dry_ingredients =
[ (Flour, 10)
, (Sugar, 5)
]
butter = (Butter, 1)
eggs = (Egg, 2)
pan = CakePan
oven_heating_task <- heatOven XXX
bowl_contents <- withBowl $ do
mapM_ add dry_ingredients
mix
add butter
whip
add eggs
waitFor oven_heating
bakeInPan pan bowl_contents
You don't even really have to understand monads to follow the above code. They also don't do much to improve it over ordinary imperative code. (They arguably do quite a bit to improve it over the "lazy streams of requests and responses" approach that early Haskell took...)
Where functional style and a robust type system becomes a huge win is in larger programs - and, in particular, significant refactors of larger programs. If you've done a few of these in a few contexts, you should have a good sense of why.
Oh, certainly there is a large aspect of "the problem is simply the examples." I have yet to see a compelling system built in the functional style, is the problem.
It is tempting to feel that there is this holy grail where this becomes a big win. I even can "intuitively" see how this would be true. However, what I also experience is that this 'win' seems countered by the 'loss' in just how much cognitive overhead goes in to building said large system. That is, sure, it can be modified with more confidence, but was it ever fully built? (I think this comes from the caveat shown in upthread paper where, to stay truly transparent the developer winds up having to account for the entire state of the system at some layer.)
Even looking at my own examples where I was somewhat proud of the amount of information that was in the type system, I can not honestly claim that the product was more bug free than loosely typed version, and it certainly wasn't more clearly communicated to my peers. (Including my future self.)
In the end, smaller more directed pieces using whichever styling makes the most sense for that section seems to be the clearest win.
Edit: Apologies for the quick edit, but I am also struck by just how well the "literate" programs I have ever read actually worked at communicating what was going on. Shame that seems to have stalled to death.
Well, I'm saying that I've very much seen it, even in the moderately sized projects I've been working on recently. And yes, they get fully built regardless of which way I'm building them.
"Even looking at my own examples where I was somewhat proud of the amount of information that was in the type system, I can not honestly claim that the product was more bug free than loosely typed version, and it certainly wasn't more clearly communicated to my peers. (Including my future self.)"
I find even in my C code that setting things up to better leverage the type system (even as limited as it is) has been a tremendous win. I'm a much stronger proponent of strong and expressive types than I am of functional programming (though I think that both provide wins in most cases).
"In the end, smaller more directed pieces using whichever styling makes the most sense for that section seems to be the clearest win."
No argument there.
"I am also struck by just how well the "literate" programs I have ever read actually worked at communicating what was going on. Shame that seems to have stalled to death."
Yeah, genuine literate programming tools are fascinating, and it's a little sad how a lot of the ideas have kind of dropped away from a core of "automatically generate docs from inline comments", which seems to have been a small part of the original. I've been toying with the notion of adding some similar functionality to some tooling I've been building, but no concrete plans yet...
Does the fact that IO forms a monad help us, when all we're doing is sequencing actions in a static order without interesting interactions? No, not really. It also doesn't hurt.
You don't even really have to understand monads to follow the above code. They also don't do much to improve it over ordinary imperative code. (They arguably do quite a bit to improve it over the "lazy streams of requests and responses" approach that early Haskell took...)Where functional style and a robust type system becomes a huge win is in larger programs - and, in particular, significant refactors of larger programs. If you've done a few of these in a few contexts, you should have a good sense of why.