'''Mutable state as such wouldn't hurt if there was a way to undo each and every language statement. In the case of an exception, everything within a try block would be rolled back before entering the catch block. In other words, YouDontWantAnExceptionYouWantaTransaction without having to code it yourself, just like SQL transactions for a systems languages. Alas, this would probably be a trifle bit inefficient... --FalkBruegmann ''' cut to activate from YouDontWantAnExceptionYouWantaTimeMachine
Such creatures exist - take CSLA for example. The trick is that you only apply the transactional approach to your business objects, which shouldn't be in such numbers that the inefficiency will cripple you.
Hasn't MooresLaw taught us and our computers that inefficient matters less and less? -- ChrisGarrod
No. MooresLaw does give us some overhead -- exponential growth. However, inefficient thinking and implementation often exhaust this capacity at a rate that is far greater than exponential. Because software systems are so layered now, the interaction of inefficient layers often combines to produce an extremely slow system. MooresLaw is a crutch. -- ArlieDavis
I'd note that this doesn't need to rely upon Moore's Law. For example, it is possible to support SoftwareTransactionalMemory (http://en.wikipedia.org/wiki/Software_transactional_memory). This is designed for safe concurrency in large operations, but is quite usable for single-processor activities. It does not support true "rollback", but it DOES wait for a commit operation prior to making the changes permanent in memory. As far as performance, STM is, in the typical case, only half the speed of normal memory operations... a penalty that can be well justified (even on slower systems) by the benefit it offers. Language support for SoftwareTransactionalMemory can make it yet more powerful, allowing its use be limited to those exact chunks of memory that require it (i.e. where it is actually utilized) and treating traditional operations as a single transaction, and possibly by using functional styles of calculation in order to KillMutableState except where it is truly required. If done properly, the penalty regarding transaction-support for mutable state can be mostly eliminated.
That said, sometimes I do want an exception. Support for transactions only allows me the option of avoiding committal when performing complex memory-only operations. However, simply because an exceptional condition occurs does not mean that I wish to fail my transaction. So, what I really want, is the ability to have 'transaction' blocks in the language that are orthogonal to the try-catch blocks. If one exits the transaction-block without commiting, the transaction automatically fails.
transaction { (* ... do stuff that might throw 'exception' or 'checked-exception' ... *) catch(checked-exception e) { do e.ignore (* exception with continuation (ContinuationPassingStyle) *)} } (* implicit commit if reached normally; NOTE: commit might fail and throw failed-commit *) catch(failed-commit f) { (* can examine reasons for failure in 'f' - likely a conflicting transaction *) (* can examine number of retries so far (f.retry-count) *) do f.retry-transaction (* exception with continuation (ContinuationPassingStyle) *) } catch(exception e) { (* at this point we're outside the transaction block, so the commit failed). *) (* ... do other stuff ... *) }Honestly, I probably want nested transaction support, too, so I can abort (not commit) just part of a transaction without aborting the whole thing, and try again with that part. This makes STM a tad more complex (and a tad less efficient), mostly in that the naming of transactions becomes hierarchical and that the logging system must handle partial commits relative only to the rest of that overarching transaction (i.e. a commit of transaction /x/y/z simply makes operations in inner-transaction 'z' part of /x/y; committing /x/y makes all of y's operations part of /x; committing /x makes all of 'x's' operations part of root - the shared data seen by everyone else.)
If you perform a mutation of state outside of any transaction, the language can support this by (essentially) wrapping it in a single transaction. Further, it can optimize it more by making that commit explicit in the message (so that, in a distributed system, only one message is delivered). Further, the language can identify unshared, local state that isn't subject to use across any transactions (such as a local integer utilized for looping) and optimize by casting away the STM overhead entirely.
See Also: SoftwareTransactionalMemory, FirstClassUndo