Monad was introduced to clean up the nesting and give it a nice syntax.
I have heard that transformer stacks can add "wrapping/unwrapping overhead", but haven't looked at it in depth, most of my Haskell work not being terribly performance sensitive at that scale.
Of for Christ's sake, I of course mean the bind and the `>>` and whatever that it desugars to. You aren't adding something to the discussion. But thanks for reminding me that syntactic sugar has no runtime overhead...
There seems to be a lot of potential for removing all cruft that comes from things like this, namely to remove the interpretive overhead. I used to think of "interpretive" as only those language executioners. But I suspect that a lot of constructs have them. Macros, or whatever mechanism you use to avoid that overhead should maybe have a place in languages that try to both be high-level and to be somewhat high performing.
You asked a question, I addressed the parts of it I could address. I don't have a full picture of what you do and don't understand, to reference in pruning my response.
If you're aware that do-notation is syntactic sugar then of course I would expect you to be aware that it has no runtime overhead - but I regularly encounter people confused on that first point. My comment adds to the discussion if you're one of those confused people (which apparently you aren't), if one of those people winds up reading the thread, and also to clarify your question for anyone else interested in answering.
My answer wasn't intended to be derisive in any way - I don't appreciate abuse in response.
mtl-style final encodings can help here, from what I understand. `mtl` has won any benchmark I've seen, over concrete transformer stacks, Free, and others.
What I do miss though (dearly) is that there is a certain cost as to what can be encapsulated. For instance, in Haskell, it is no longer so easy to hide side effects behind a pure interface.
This is not true. There are a number of ways to do this in Haskell.
Also this is tantamount to, "my programming language won't let me lie", and is often associated with an infacility with Haskell. I mean no offense, just speaking from experience.
You don't need to hide effects behind a pure interface once you're comfortable with the abstractions provided.
Edit: tel's comment is quite good as-is, but I'd like to add that part of what makes ST nice isn't just having an eliminator, but also so you have a more precise delineation of what "effects" could happen. IO is kind of a big type. Still very valuable.
Now I know that you can hide unsafePerformIO in the interface, and in this case I expect it is fine, but this is not easy, it feels like a hack.
Another example that I have personally watched play itself out in disbelief was in a fairly large Haskell project: There was need of a lookup table, with some 10000 elements. In such cases, I would have stored the lookup in a database, or a separate file at least, but because they wanted to maintain purity so badly, the solution was to write a 10000 line long Haskell file. But wouldn't it be nice if you could load a file and tell Haskell: don't worry, this won't change, so treat reads to this purely.
I never said such things were impossible, merely not easy, and I don't believe this is by accident. I am almost certain that such things are made difficult to prevent abusing such features, but that doesn't stop me from missing them :) .
import Control.Monad.ST
import Data.STRef
data Counter s = Counter { get :: ST s a, inc :: ST s () }
mkCounter :: STRef s Int -> Counter s
mkCounter v = Counter (readSTRef v) (incRef v) where
incRef ref = do
i <- readSTRef v
writeSTRef v (i + 1)
A little noisier since we note that reading the ref is an impure action and we track a phantom `s` parameter around in order to convince Haskell that certain uses of counters are pure. Now comp :: ST s Int
comp = do
v <- newSTRef 0
let c = mkCounter v
inc c
inc c
inc c
get c
This is technically a pure computation despite using an impure mechanism internally. We can get Haskell to prove this for us pureComp :: Int
pureComp = runST comp
Here `runST :: (forall s . ST s a) -> a` completes the proof that if the `s` variables in a computation are completely undetermined then that computation must be pure.