(and (eq? (extract a (make-safe a b)) b) (or (eq? a b) (not (extract b (make-safe a c)))) (or (safe? a) (not (extract a b))) (safe? (make-safe a b)) (safe? (make-safe a b) a) (boolean? (safe? a)) (boolean? (safe? a b)) (eq? (eq? a b) (safe? (make-safe a c) b)))
The holder of a brand is able to extract the content from any safe with that brand that he finds. For now (extract a b) yields false if b is not a safe. REPLs for Scheme have various ways of reporting domain errors for primitives. In practice users of extract can use the more discriminating safe? if someone should store #f in a safe. The only runtime cost is when this is the case.
The garbage collector needs to learn about the safe. If it encounters a safe the value therein should not be preserved unless the corresponding brand is found elsewhere. This implies a list of deferred decisions about values found in safes.
I discuss another pattern, called the sealer-unsealer, here. Both these proposed primitives support the other synergy patterns and each other efficiently as well.
The following defines make-safe, extract and safe? in terms of new-seal and eq?.
(define ctool (let ((s (new-seal))) (let ((seal (car s)) (unseal (cadr s)) (sealed? (caddr s))) (let ((make-safe (lambda (a b) (seal (cons a b)))) (extract (lambda (b p) (and (sealed? p) (let ((c (unseal p))) (and (eq? (car c) b) (cdr c)))))) (safe? (lambda (a . l) (and (sealed? a) (or (null? l) (and (pair? l) (null? (cdr l)) (eq? (car l) (car (unseal a))))))))) (list make-safe extract safe?))))) (define make-safe (car ctool)) (define extract (cadr ctool)) (define safe? (caddr ctool))
The following defines eq? in terms of make-safe and extract:
(define (eq? a b) (extract b (make-safe a #t)))We have now the following set of reductions:
The following defines new-seal in terms of make-safe, extract and safe?:
(define (new-seal) (let ((S (cons '() '()))) (list (lambda (rep) (make-safe S rep)) ; seal (lambda (abs) (or (extract S abs) (if (safe? abs S) #f (error "invalid argument" abs)))) ; unseal (lambda (x) (safe? x S))))) ; => (seal unseal sealed?)On 2012 Nov 3 Darius Bacon noted that a previous proposal, without safe?, could not seal the value #f. That is now fixed, but with a bit of extra complexity. On 2014 May 7, Darius noted that my derivation of extract relied on eq?; the derivations are not cyclic. Perhaps the safe primitives are stronger in that sense than the sealer. Some think that a language should not have eq? and they might prefer the sealer for that reason.
The following would seem, at first, to be a slight modification and simplification to the safe, which is trivially implemented in current Scheme!
(define (make-safe a b) (lambda (x) (and (eq? x a) b)))The modified Guttag definition is:
(let ((s (make-safe a b))) (and (eq? (s a) b) (or (not (s x)) (eq? a x)))) => #tThere seems to be an unsurmountable problem! How does the legitimate holder of the brand know that a value of uncertain provenance is confined? Perhaps it is instead (lambda (x) (set! y x)) where y is some variable in scope of the perpetrator who is attempting to open thereby safes not meant for him.
A Scheme value is a pair of behavior and state. If the behavior could be ascertained by the holder this problem would be solved. That breaks other security properties however. If it could be ascertained that the behavior was specifically “(lambda (x) (and (eq? x _) _))” then that too would solve the problem. Even that is dangerous.