Arc Forumnew | comments | leaders | submitlogin
Rudimentary cons counter
3 points by fallintothis 3534 days ago | 7 comments
I've written this code in two threads now (http://arclanguage.org/item?id=11129 and http://arclanguage.org/item?id=11104) to test memory efficiency by counting how many times cons is called.

  arc> (= cons-count* 0)
  0
  arc> (let old-cons cons
         (def cons (x y)
           (++ cons-count*)
           (old-cons x y)))
  *** redefining cons
  #<procedure: cons>
This gets tedious because it clobbers cons and you need to restart your Arc session to get it back. So, a quick & dirty utility is born.

  (mac count-cons (expr)
    (w/uniq (old-cons cons-count)
      `(with (,old-cons cons ,cons-count 0)
         (= cons (fn (x y) (++ ,cons-count) (,old-cons x y)))
         (after (do1 ,expr
                     (prn "consed " (plural ,cons-count "time") "."))
           (= cons ,old-cons)))))
It'll bog down run times, but it might be useful. It's even the beginning of a really simple call-counting profiler, if someone wants to explore the idea. Examples:

  arc> (count-cons (cons 1 2))
  consed 1 time.
  (1 . 2)
  arc> (count-cons (map [apply + _] (pair '(1 3 5 7 9))))
  consed 11 times.
  (4 12 9)
  arc> (count-cons (pair '(1 3 5 7 9) +))
  consed 4 times.
  (4 12 (9))
Feel free to improve upon this. I don't know if there are any glaring issues like thread-safety or something because I just jammed this out.


1 point by akkartik 3533 days ago | link

My refactoring with several general utilities:

  (mac init args
    `(unless (bound ',(car args))
       (= ,@args)))

  (mac after* block
    (let (body finally) (split-by block ':do)
      `(after
         (do ,@body)
         (do ,@finally))))

  (mac redef(var expr)
    `(after*
       (set disable-redef-warnings*)
       (= ,var ,expr)
      :do
       (wipe disable-redef-warnings*)))

  (mac after-exec(fnname args . body)
    `(let old ,fnname
        (redef ,fnname
              (fn ,args
                (let result (old ,@args)
                  ,@body
                  result)))))

  ;; dynamic scope when writing tests
  (mac shadow(var expr)
    (let stack (globalize stringify.var "-stack")
      `(do
         (init ,stack ())
         (push ,var ,stack)
         (redef ,var ,expr))))

  (mac unshadow(var)
    (let stack (globalize stringify.var "-stack")
      `(do
        (if (or (~bound ',stack)
                (empty ,stack))
           (prn "*** couldn't unshadow " ',var)
           (redef ,var (pop ,stack)))
        nil)))

  (mac shadowing(var expr . body)
    `(after*
       (shadow ,var ,expr)
       ,@body
      :do
       (unshadow ,var)))

  (mac count-cons(expr)
    (w/uniq cons-count
    `(shadowing cons cons
        (let ,cons-count 0
          (after-exec cons(x y)
            (++ ,cons-count))
          (after
            ,expr
            (prn "consed " (plural ,cons-count "time") "."))))))

  (def split-by(seq delim)
    (case type.seq
      cons    (split seq (or (pos delim seq) (len seq)))
      string  (split seq (or (posmatch delim seq) (len seq)))
              (err "bad type for split-by")))

-----

1 point by fallintothis 3519 days ago | link

Rather random thing to comment on after 2 weeks, I know. I was just reading this and noticed that your redef macro isn't really doing much: safeset doesn't get called by = unless you happen to make a defset to that effect, which seems unlikely.

  arc> (def f (x) (+ x 1))
  #<procedure: f>
  arc> (= f [+ _ 2])       ; no redef warning
  #<procedure: f>
  arc> (def f (x) (+ x 3)) ; redef warning
  *** redefining f
  #<procedure: f>

  arc> (defset blah (x)
         (w/uniq g
           (list (list g x)
                 x
                 `(fn (val) (safeset ,x val)))))
  #<procedure>
  arc> (= x 100)
  100
  arc> (= x 200)        ; no redef warning
  200
  arc> (= (blah x) 300) ; redef warning
  *** redefining x
  300
Still an interesting idea to selectively silence safeset, though.

-----

1 point by akkartik 3517 days ago | link

Ah, that's embarrassing :)

So all I need is this then without any change to safeset:

  (mac redef(f args . body) `(= ,f (fn ,args ,@body)))

-----

1 point by fallintothis 3517 days ago | link

Right. So then after-exec becomes (keeping the presumably-intended variable capture)

  (mac after-exec (fnname args . body)
    `(let old ,fnname
       (redef ,fnname ,args
         (let result (old ,@args)
           ,@body
           result))))
And your uses of redef when you aren't redefining a function should be changed to = (e.g., in shadow).

It might still be cool to toggle safeset warnings. With Arc's single namespace, it's annoying to

  (load "foo.arc")
just to have it spit out redefinition warnings. But silencing safeset isn't really the answer to that problem.

-----

1 point by akkartik 3517 days ago | link

Yes. When I updated anarki I decided to just do

  (= redef =) ; how pretty!
It seems plausible to want redefs to look different from defs.

Disabling these warnings on reload is useful, but a separate use case. I tend to ignore them on reload, but I don't want to see them when I startup lest I start ignoring them altogether.

-----

1 point by akkartik 3533 days ago | link

redef controls whether the "redefining var" warnings are displayed. I changed the safeset macro thus at the start of arc.arc:

  (assign disable-redef-warnings* nil)
  (assign safeset (annotate 'mac
                    (fn (var val)
                      `(do (if (is nil disable-redef-warnings*)
                           (if (bound ',var)
                               (do (disp "*** redefining " (stderr))
                                   (disp ',var (stderr))
                                   (disp #\newline (stderr)))))
                           (assign ,var ,val)))))

-----

1 point by akkartik 3534 days ago | link

Off-topic: I've found myself using (after (do ..)) a few times and always found it clumsy. I just got around to do something about it.

  (mac after* block
    (let (body finally) (split-by block ':do)
      `(after
         (do ,@body)
         (do ,@finally))))

  (def split-by(seq delim)
    (case type.seq
      cons    (split seq (or (pos delim seq) (len seq)))
      string  (split seq (or (posmatch delim seq) (len seq)))
              (err "bad type for split-by")))

  arc> (after* (prn "interrupted") (/ 3 0) (prn "completed") 34
  :do
    (prn "end") (prn "and again"))
  interrupted
  end
  and again
  Error: "/: division by zero"
Never again will do seem in the wrong place..

-----