Arc Forumnew | comments | leaders | submitlogin
More Fun With Type Systems: defm
7 points by nex3 6101 days ago | 5 comments
This is a reasonably simple abstraction over the common task of using redef to extend already-existing methods to accept new types. It's the same idea as generic methods: you specify types for one or more arguments, and your new function (rather than the old definition) is called if those types are given. The syntax is similar to optional arguments: (t arg typename).

Here's the code (it's also in lib/defm.arc on Anarki):

  (def argmap (symf annf args)
    (map (afn (arg)
           (if (isa arg 'sym) (symf arg)
               (atom (cadr arg)) (annf arg)
               (annf `(,(car arg) ,(self (cadr arg)) ,@(cddr arg)))))
         args))

  (mac defm (name args . body)
    `(redef ,name ,(argmap idfn [if (is (car _) 't) (cadr _) _]
                           args)
       (if (and ,@(map (fn (decl) `(isa ,(car decl) ',(cadr decl)))
                       (trues (argmap [idfn nil]
                                      [if (is (car _) 't) (cdr _) (cadr _)]
                                      args))))
           (do ,@body)
           (old ,@(argmap idfn [cadr _] args)))))
As an example, here's last defined to work on strings:

  (defm last ((t str string))
    (str (- (len str) 1)))


2 points by almkglor 6100 days ago | link

  ; edit - made scanner and make-scanner different
  (def make-scanner args
    (dsb (&k car cdr) args
      (annotate 'scanner (cons car cdr))))

  (defm car ((t c scanner))
    ((car (rep c))))

  (defm cdr ((t c scanner))
    ((cdr (rep c))))

  ;(defm isa ((t c scanner) typ)
  ;  (if (is typ 'cons)    t
  ;      (is typ 'scanner) t))
  (redef isa (c typ)
    (if (is (type c) 'scanner)
        (in typ 'scanner 'cons)
        (old c typ)))

  (def scanner (s . args)
    (err:tostring:write "Unknown scanner type" (type s)))

  (defm scanner ((t s string) (o start 0) (o end (len s)))
    (let (a d d-valid) nil
      (make-scanner
        'car (fn () (or a (= a (s start))))
        'cdr (fn ()
               (if d-valid
                   d
                   (= d-valid t
                      d (if (is (+ 1 start) end)
                            nil
                            (scanner s (+ 1 start) end))))))))

  ; input port
  (defm scanner ((t s input))
    (let a (readc s)
      (if (no a)
          nil
          (let (d d-valid) nil
            (make-scanner
              'car (fn () a)
              'cdr (fn ()
                     (if d-valid
                         d
                         (= d-valid t
                            d (scanner s)))))))))
^^ strings as lists, indeed ^^

Hmm. Another idea: make redef bind 'old to (fn args nil) if the functions hasn't been defined yet. Possibly just replace the (let old ,name ...) with (let old (or (errsafe ,name) (fn args nil)) ...)

edit: Hmm, probably should make redef define the function using (fn args (let ,parms args ...)) instead, so that various functions can pass arbitrarily. Or add a 'pass function which just passes the old call completely.

-----

2 points by nex3 6100 days ago | link

Yeah, that's exactly the sort of thing I was thinking.

As for old functions, I'd really like to have a better way than "errsafe" for determining whether or not a variable is bound. But yeah, I agree this should work for not-yet-defined functions.

-----

2 points by nex3 6100 days ago | link

Well, don't I feel stupid. There's a bound function that does just that.

Edit: Okay, using bound, I updated redef (and thus defm) to not die if the function isn't already defined. See the following Anarki commits: http://git.nex-3.com/arc-wiki.git?a=commit;h=b8a2edb218634f3..., http://git.nex-3.com/arc-wiki.git?a=commit;h=d1fa9fd5d1f3eb1..., and http://git.nex-3.com/arc-wiki.git?a=commit;h=68f1a03eb4f07e5....

-----

1 point by almkglor 6100 days ago | link

Hmm, current version does not work with rest parameters/dotted lists ^^

Hmm.

-----

1 point by nex3 6100 days ago | link

Oh, yeah, I meant to mention that. It's because of extensive use of map, which sort of chokes and dies on improper lists.

-----