git @ Cat's Eye Technologies define-opaque / master eg / proper-list.scm
master

Tree @master (Download .tar.gz)

proper-list.scm @masterraw · history · blame

; SPDX-FileCopyrightText: In 2023, Chris Pressey, the original author of this work, placed it into the public domain.
; For more information, please refer to <https://unlicense.org/>
; SPDX-License-Identifier: Unlicense

; example usage with Chicken Scheme: csi -q -b proper-list.scm

(load "define-opaque-0.3.scm")

; Define a "proper list" data structure, which is either:
; a) nil, or
; b) a cons cell where the tail contains a proper list.

; Part 1.  We'll use `define-opaque-object`, which will get us partway there,
; but as we'll show later, is insufficient to guarantee the proper list property.

(define-opaque-object proper-list make-proper-list (selector value) ('nil 0)
  (
    (proper-list? (lambda ()
      #t))
    (nil (lambda ()
      (make-proper-list 'nil 0)))
    (cons (lambda (head-value tail-value)
      ; NB this is not hugely great, as it's a kind of duck-typing.
      (if (tail-value 'proper-list?)
        (make-proper-list 'cons (cons head-value tail-value))
        (error "Tail is not a proper list: " tail-value))))
    (repr (lambda ()
      (cond
        ((equal? selector 'nil)
          '())
        ((equal? selector 'cons)
          (let* ((head (car value)) (tail (cdr value)))
            (cons head (tail 'repr)))))))
  )
)

(define demo1 (lambda ()
  (let* (
    (list0 (proper-list 'cons 123 (proper-list 'cons 456 (proper-list 'nil))))
  )
    (display (list0 'repr)) (newline)
  )))

(demo1)

(define errorful-demo1 (lambda ()
  (let* (
    (list0 (proper-list 'cons 123 (proper-list 'cons 456 (proper-list 'nil))))
  )
    (display (proper-list 'cons 123 456))
  )))

; Part 2.  Show that we can forge a bogus proper list and trick our
; proper list object into allowing creation of an improper list.

(define-opaque-object forged-list make-forged-list (dummy) (#f)
  (
    (proper-list? (lambda ()
      #t))                 ; lies!
    (repr (lambda ()
      42))                 ; not a list at all
  )
)

(define forgery forged-list)

(define demo2 (lambda ()
  (let* (
    (list0 (proper-list 'cons 123 (proper-list 'cons 456 (proper-list 'nil))))
    (bad-list (proper-list 'cons 789 forgery))
  )
    ; notice the tail is not a list or nil
    (display (bad-list 'repr)) (newline))
))

(demo2)

; Part 3.  Define a proper list data structure using `define-opaque-adt`
; and show that it enforces the desired property of proper lists.

(define-opaque-adt proper-list-adt make-proper-list-adt open-proper-list-adt
  priv '(nil . 0)
  (
    (nil (lambda ()
      (make-proper-list-adt '(nil . 0))))
    (cons (lambda (head-value tail-value)
      (let* ((tail-priv (open-proper-list-adt tail-value)))
        (make-proper-list-adt (cons 'cons (cons head-value tail-value))))))
    (repr (lambda ()
      (cond
        ((equal? (car priv) 'nil)
          '())
        ((equal? (car priv) 'cons)
          (let* ((head (car (cdr priv))) (tail (cdr (cdr priv))))
            (cons head (tail 'repr)))))))
  )
)

(define demo3 (lambda ()
  (let* (
    (list0 (proper-list-adt 'cons 123 (proper-list-adt 'cons 456 (proper-list-adt 'nil))))
  )
    (display (list0 'repr)) (newline))
))

(demo3)

(define errorful-demo3 (lambda ()
  (let* (
    (list0 (proper-list-adt 'cons 123 (proper-list-adt 'cons 456 (proper-list-adt 'nil))))
  )
    (proper-list-adt 'cons 789 forgery))
))