Arc Forumnew | comments | leaders | submitlogin
2 points by almkglor 6100 days ago | link | parent

Attaching a setterfunction to an object allows us to create encapsulating modules which can have specific module variables modified:

  (with (var1 42
         fn1 nil)
    (def fn1 (x)
      (prn var1 ": " x))
    (add-attachments
      '= (fn (v s)
           (case s
             var1 (= var1 v)
                  (err:string "Cannot set module variable: " s)))
      'keys (fn () (list 'var1 'fn1)
      (fn (s)
        (case s
          fn1 fn1
          var1 var1)))))
Note that the above does not even care about generating its own type, because it's really a one-of table.

Also, making use of lexical environment closures severely reduces the amount of code necessary for accessor functions:

  ;Using attachments -
  (def bidir-table ()
    " Creates a bidirectional table.  Works like a normal
      table but returns keys when queried with values.
      See also [[table]] "
    (with (k-to-v (table)
           v-to-k (table))
      (add-attachments
        '= (afn (v k)
             ; determine if delete or assign
             (aif
               ; insert new pair
               v
                 (do
                   ; delete any existing pairs first
                   (self nil k)
                   (self nil v)
                   ; add it
                   ;  no point assigning this to v-to-k
                   ;  if v==k, since k-to-v will return
                   ;  that mapping first
                   (if (isnt k v) (= (v-to-k v) k))
                   (= (k-to-v k) v))
               ; deleted k
               (k-to-v k)
                 (= (v-to-k it) nil
                    (k-to-v k) nil)
               ; deleted v
               (v-to-k k)
                 (= (k-to-v it) nil
                    (v-to-k k) nil)))
        ; Only return items which were assigned as
        ; keys, so that 'ontable doesn't go over
        ; pairings twice.
        'keys (fn () (keys k-to-v))
        (annotate 'table
          (fn (k) (or (k-to-v k) (v-to-k k)))))))

  ;Using defset-type and defcall:
  (define bidir-table ()
    " Creates a bidirectional table.  Works like a normal
      table but returns keys when queried with values.
      See also [[table]] "
    (annotate 'bidir-table (list (table) (table))))

  (defcall bidir-table (c k)
    (let (k-to-v v-to-k) (rep c)
      (or (k-to-v k) (v-to-k k))))

  (defset-type bidir-table (c v k)
    (let (k-to-v v-to-k) (rep c)
      ((afn ()
             ; determine if delete or assign
             (aif
               ; insert new pair
               v
                 (do
                   ; delete any existing pairs first
                   (self nil k)
                   (self nil v)
                   ; add it
                   ;  no point assigning this to v-to-k
                   ;  if v==k, since k-to-v will return
                   ;  that mapping first
                   (if (isnt k v) (= (v-to-k v) k))
                   (= (k-to-v k) v))
               ; deleted k
               (k-to-v k)
                 (= (v-to-k it) nil
                    (k-to-v k) nil)
               ; deleted v
               (v-to-k k)
                 (= (k-to-v it) nil
                    (v-to-k k) nil)))))