;;; Catbird Game Engine ;;; Copyright © 2022 David Thompson ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. ;;; You may obtain a copy of the License at ;;; ;;; http://www.apache.org/licenses/LICENSE-2.0 ;;; ;;; Unless required by applicable law or agreed to in writing, software ;;; distributed under the License is distributed on an "AS IS" BASIS, ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;;; See the License for the specific language governing permissions and ;;; limitations under the License. ;;; Commentary: ;; ;; Slots that notify instances upon modification. ;; ;;; Code: (define-module (catbird observer) #:use-module (catbird config) #:use-module (oop goops) #:export ( on-change)) ;; This is a hack to deal with the fact that specializing GOOPS ;; accessors does not compose with inheritance. ;; ;; See ;; https://dthompson.us/issues-with-object-oriented-programming-in-guile.html ;; for details. (define-class ()) (define-generic on-change) (define-method (observer-slot? (slot )) (get-keyword #:observe? (slot-definition-options slot))) (define-method (compute-setter-method (class ) slot) (if (observer-slot? slot) ;; Wrap the original setter procedure with a new procedure that ;; calls the on-change method. (make #:specializers (list class ) #:procedure (let ((slot-name (slot-definition-name slot)) (proc (method-procedure (next-method)))) (lambda (obj new) (let ((old (and (slot-bound? obj slot-name) (slot-ref obj slot-name)))) (proc obj new) (on-change obj slot-name old new))))) (next-method))) (define-class () #:metaclass )