Hey everyone,
Today, I added `tlet' a There is a slight problem with our previous higher-order function:
(deftransaction sleep-after (seconds transaction) transaction (trans (sleep seconds)))
The return value of the new transaction is not the same as the return value of the input transaction. Instead what `sleep-after' does is always return NIL, because `sleep' returns NIL. What we need is a way to bind the value of `transaction' to a variable. Hence `tlet'. Here is how we could write `sleep-after' correctly:
(deftransaction sleep-after (seconds transaction) (tlet ((val transaction)) (trans (sleep seconds)) (trans val)))
Now if you looked through the debugging output when typing `(perform (sleep-after 2 (increment *c*)))', with both versions, you will see a line looking like:
20:33 STM-LOGGER/+DRIBBLE+: Transaction finished executing with: 6
or, with the old version:
20:33 STM-LOGGER/+DRIBBLE+: Transaction finished executing with: NIL
If you were very observant, you would have noticed that the `tlet' in `sleep-after' could be replaced with a `prog1'-like construct. We can define `progt1' in terms of `tlet':
(defmacro progt1 (&body body) (with-unique-names (val) `(tlet ((,val (trans ,(car body)))) ,@(cdr body) (trans ,val))))
And thus `sleep-after' becomes:
(deftransaction sleep-after (seconds transaction) (progt1 transaction (trans (sleep seconds))))
which is much nicer. However, we quickly run into a problem. We now have to define a whole slew of transactional equivalents of `prog2', `multiple-value-prog1', and so on. When translating an the MVar example from Haskell to Common Lisp, I even had to write a new version of `if'!
The next step is to write a code-walker that converts Common Lisp forms into the transactional equivalent.
Hoan