#!r6rs
(library (obj-lecture queue1-encapsulated)
  (export empty snoc isEmpty head tail)
  (import (rnrs))
  
  ;; A Queue is an Abstract Value
  
  ;; Internally, a Queue is a (HiddenToken -> Listof[SchemeValue]
  ;; where HiddenToken is a singleton that no other code
  ;; knows about.
  
  ;; concrete->abstract : Listof[SchemeValue] -> Queue
  ;; abstract->concrete : Queue -> Listof[SchemeValue]
  ;; (see bottom of file for how to hide the token in library-less languages)
  (define hidden-token (cons 'unique 'and-hidden))
  (define (concrete->abstract lst)
    (let ((an-abstract-queue
           (lambda (token)
             (if (eq? token hidden-token)
                 lst
                 (error 'queue1-encapsulated
                        "unauthorized-access-attempt")))))
      an-abstract-queue))
  (define (abstract->concrete q)
    (q hidden-token))


  ;; empty : -> Queue
  (define (empty) 
    (concrete->abstract  
     '()))
  
  ;; snoc : Queue SchemeValue -> Queue
  (define (snoc q v)
    (concrete->abstract 
     (append (abstract->concrete q) (list v))))
  
  ;; isEmpty : Queue -> Boolean
  (define (isEmpty q)
    (null? (abstract->concrete q)))
  
  ;; head : Queue -> SchemeValue
  (define (head q)
    (car (abstract->concrete q)))
  
  ;; tail : Queue -> Queue
  (define (tail q)
    (concrete->abstract (cdr (abstract->concrete q)))))
#|
  ;; (Code below illustrates how one might hide the hidden-token
  ;;  outside of a library form; e.g. in R5RS Scheme.  The code must
  ;;  be run in a context where definitions are evaluated from top
  ;;  to bottom order.  (It will also work inside of a library form.)
  (define c->a)
  (define a->c)
  (define dummy 
    (let ((hidden-token ;; binds *the* HiddenToken
           (cons 'unique 'and-hidden)))
      (set! c->a
            (lambda (lst)
              (let ((an-abstract-queue
                     (lambda (token)
                       (if (eq? token hidden-token)
                           lst
                           (error 'queue1-encapsulated
                                  "unauthorized-access-attempt")))))
                an-abstract-queue)))
      (set! a->c
            (lambda (q)
              (q hidden-token)))))
  (define concrete->abstract c->a)
  (define abstract->concrete a->c)
|#