; 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))
))