Next: , Previous: , Up: Tutorial   [Contents]


4.6 Transactions make errors survivable

Mistakes happen, and when they do, we’d like damage to be minimal. But with many moving parts, accomplishing this can be difficult.

However, Goblins makes our life easier. To see how, let’s intentionally insert a couple of print debugging lines (with pk, which is pronounced and means “peek”) and then an error:

(define (^borked-cgreeter _bcom our-name)
  (define times-called
    (spawn ^mcell 0))
  (methods
    ((get-times-called)
     ($ times-called 'get))
    ((greet your-name)
     (pk 'before-incr ($ times-called 'get))
     ;; increase the number of times called
     ($ times-called 'set
        (+ 1 ($ times-called 'get)))
     (pk 'after-incr ($ times-called 'get))
     (error "Yikes")
     (format #f "[~a] Hello ~a, my name is ~a!"
             ($ times-called 'get)
             your-name our-name))))

Now let’s spawn this friend and invoke it:

goblins[1]> (define horatio
              (spawn ^borked-cgreeter "Horatio"))
goblins[1]> ($ horatio 'get-times-called)
; => 0
goblins[1]> ($ horatio 'greet "Hamlet")
; pk debug: (before-incr 0)
; pk debug: (after-incr 1)
; ice-9/boot-9.scm:1685:16: In procedure raise-exception:
;   Yikes
; Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.

Whoops! Looks like something went wrong! We can see from the pk debugging that the times-called cell should be incremented to 1. And yet…

goblins[1]> ($ horatio 'get-times-called)
; => 0

We will cover this in greater detail later, but the core idea here is that synchronous operations run with $ are all done together as one transaction. If an unhandled error occurs, any state changes resulting from synchronous operations within that transaction will simply not be committed. This is useful, because it means most otherwise difficult cleanup steps are handled automatically.