This is way to much simplification. The problem with STM is that it is very hard to integrate in classical languages. In Clojure and Haskell STM works really well and STM is just in production.
You can still write pure functions even if they aren't enforced by static typing, right? What do you mean by "IO effects hidden behind seemingly innocuous evalation"?
Clojure's data structures are immutable, and I don't think you're supposed to use STM with any mutable objects or data. You pass pure functions to the STM system that take an old (immutable) value and return a new (immutable) value. They are pure though, so they can be called multiple times in a transaction.
I'm sure I'm making a muddle of it explaining it though, and I'm not sure what you concerns are specifically. So I highly recommend reading the ants.clj demo and watching the accompanying video with a detailed explanation by Rich Hickey, it helped me understand how STM works specifically in Clojure better than any other source, including two books on Clojure. I don't have experience with Haskell's or any other STM though, and I'm really just a novice with Clojure, so YMMV, etc.
STM needs a guarantee that all actions within are reversible. If you can launchTheMissiles in the STM transaction, then the STM cannot guarantee the desired properties.
One of the nice things in Haskell is that effects are typed. So STM can require its given actions to only have STM effects, and disallow full-fledged IO effects.
If you're willing to go by convention without compiler guarantees, then I'm not sure what advantage Clojure has over Java here, as an example?
- There is a IO macro that you can use to signal "Hey IO is going on you cant do this". I have jet to see this beeing used in production.
- Only Refs can change in Clojure (would be diffrent if you would have it in java)
- Refs are not really used that often. (in java everything changes all the time)
- Its easy to see/know if a function is dangerous because of naming conventions and other coding conventions
So why does "by convention" work out in Clojure and not in C#:
- Standard library is written with this in mind.
- Immutable Data structures are standard.
- The conventions where there since the language was created and get inforced both by the community and the language.
So you are right in terms of compiler guarantees you don't have much more in clojure then you have in Java but in the end it on the programmer that he writtes something that works. The stuff mention above seem to make it easy enought to work with them that people don't have a problem creating something that works with the STM.