Also, as someone in the Haskell industry, I think you're wrong about trends. The last 5 or so years have been an accelerating trend of Haskell adoption. Of course it is still dwarfed by most other languages, but the number of multi-million dollar contracts I've seen executed in Haskell has been multiplying.
>Also, as someone in the Haskell industry, I think you're wrong about trends. The last 5 or so years have been an accelerating trend of Haskell adoption. Of course it is still dwarfed by most other languages, but the number of multi-million dollar contracts I've seen executed in Haskell has been multiplying.
That's my point: that's the hype cycle, and it's now peaking as people realise what a lot of effort it is to actually use monads in reality with the awful complexity of monad transformer stacks and such.
Simple example, if you have a list containing functions ([Reader r a]) it forms an Applicative fine as they compose. However you can't satisfy the conditions needed to form a Monad.
When there's several possible distributive laws or none, that's important. It means that either the interactions between the two effects are subtle and need to be further specified or they are completely incompatible.
Algebraic effects can only handle effects that trivially commute. So monad composition is a finer, thus more expressive operation.
Expressing effects using monad transformer stacks might be more expressive and allow finer-grained distinctions, or whatever (please elaborate what you mean, I'm interested), but it's syntactically ugly (lift), it's confusing, and it's not clear that the expressiveness you get is practically useful.
In contrast, algebraic effects might 'only handle effects that trivially commute' (again, going to need more detail on this), but they're easy to understand and easy to use. They're much more likely to be a practically usable effect system that real programmers can really use in the real world in a way that doesn't convolute code with details of how you compose effects unduly.
Which leads to code that needs to be puzzled out, rather than code that flows naturally. 'What does [arbitrary-monad-operationM_] mean for SomeMonadTransformerStack again???' is all too common in my experience.
I really think that's false abstraction, like an 'object' superclass in OO languages.
And once you learn them, it's a lot easier to see what code using them does at a glance, rather than reading the corresponding for loop to work out what it does, and whether it has any side effects.
It's trivial to read a loop, because it's right there. It's trivial to tell if it has any effects, because they're notated right there as algebraic effects.
It means you're mapping over something and collecting the results. Yes you need a little more information to understand exactly what you're mapping over or what "collect" means in this context, but this is still more information that `for` gives you without reading more.
Although I find in practice it's usually fairly obvious what `forM` means in context, because you're likely writing a lot of code working with the same monads.
>they're notated right there as algebraic effects.
I'm not sure what you mean by this. Unless you're using some kind of advanced imperative language with effect types, they're not "right there", you have to infer them from the code.
Whereas with Haskell you usually have a type signature which tells you what the effects are.
Haskell and other pure languages let you compute values independent of the monad its computed in, which is very useful. Of course, this is all conceptual, eventually everything is translated to underlying CPU monad and its implicit join.
What on earth are you modelling a CPU as? What thing a CPU does corresponds to join?
"It's all just the CPU monad" is just.. inane. It's like saying 'C functions are pure functions that implicitly take and return the world'. Maybe? But at that point 'pure' has lost all of its information content.