素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: twitter: @javascripter にどうぞ。

オブジェクトシステム

(define *table* (make-eq-hashtable))

(define boot-object
  (lambda (object)
    (if (not (hashtable-contains? *table* object))
      (let ((proto (make-eq-hashtable)))
        (hashtable-set! proto '__proto__ #f)
        (hashtable-set! *table* object proto)))))

(define property-hashtable-ref
  (lambda (object)
    (boot-object object)
    (hashtable-ref *table* object 'not-found)))

(define property-get
  (lambda (object property)
    (hashtable-ref (property-hashtable-ref object) property 'not-found)))

(define property-set!
  (lambda (object property value)
    (hashtable-set! (property-hashtable-ref object) property value)))

(define ->
  (lambda (object property)
    (let ((value (property-get object property))
          (proto (property-get object '__proto__)))
      (if (and proto (eq? value 'not-found))
        (-> proto property)
        value))))


(define make-plain-object
  (lambda ()
    (let ((object (lambda () #f)))
      object)))

(define make-constructor
  (lambda (proc)
    (let ((constructor (lambda (this) (proc this) this))
          (prototype (make-plain-object)))
    (property-set! constructor 'prototype prototype)
    (property-set! prototype 'constructor constructor)
    constructor)))

(define allocate
  (lambda (constructor)
    (let ((object (make-plain-object)))
      (property-set! object '__proto__ (-> constructor 'prototype))
      object)))

(define new
  (lambda (constructor)
    (let ((this (allocate constructor)))
      (constructor this))))

(define instance-of?
  (lambda (object constructor)
    (or (eq? (-> object 'constructor) constructor)
      (let ((proto (-> object '__proto__)))
        (and proto (instance-of? proto constructor))))))

(define <object>
  (make-constructor values))

(define object (new <object>))
(property-set! object 'foo 123)

(define new-object (new <object>))
(property-set! new-object '__proto__ object)

(display (-> new-object 'foo)) ; 123
(newline)

(property-set! new-object 'foo 456)

(display (-> new-object 'foo)) ; 456
(newline)

(display (-> object 'foo)) ; 123
(newline)

いっぺんにプロパティを設定するために、properties-set!関数を作ろうと思ったけど、途中で飽きた。
下は、その時できた副産物。

(define-syntax define-hashtable
  (syntax-rules ()
    ((_ (key value) ...)
     (let ((hashtable (make-eq-hashtable)))
       (hashtable-set! hashtable key value) ...
       hashtable))))

こんな感じに使う。

(define h
  (define-hashtable
    ('foo 123)
    ('bar 345)
    ('baz 567)))

(display (hashtable-ref h 'foo #f)) ; => 123
(newline)