Arc Forumnew | comments | leaders | submitlogin
aget: apply special syntaxes to association lists
4 points by thaddeus 2684 days ago | 3 comments
I'm just fooling around as you can probably see. I don't fully understand how macros really work and I'm pretty sure I am doing it wrong since I needed to use 'eval' inside a macro.

Redefined the 'alref' function as such:

  (mac aget (al (o key nil))
    `(if ,key 
	   (cadr (assoc ,key ,al))
	   (withs (xpn (ssexpand ',al)
	    	   al  (eval (car xpn))
                   key (eval (cadr xpn)))
	      (cadr (assoc key al)))))	
Usage:

  arc> (= x '((first "First Name")(last "Last Name")))
   ((first "First Name") (last "Last Name"))
  arc> (aget x 'first)
   "First Name"
  arc> (aget x!first)
   "First Name"

And I really shouldn't be doing the following since I really don't know the consequences, but oh well..... it's only for fun.

Hacked more of ac.scm: http://www.blackstag.com/docs/hacked.ac.scm.arc

So now I can:

  arc> ^x!first
   "First Name"
(and I've run out of special characters lol, but I figure '^' looks like a mini 'A' for association list anyway)

note: I haven't figured this one out yet...

  arc> (= ^x!first "New Name")	
    Error: "reference to undefined identifier: _if"
but I'm tapped out for the night....

T :)



3 points by rocketnia 2683 days ago | link

The reason = doesn't work is that

  (aget x!first)
expands to

  (if nil (cadr ...) (withs ...))
and I guess = doesn't support assigning to an 'if expression. (I wonder if it could....)

Instead of expanding into (if ...), aget could instead expand only into (cadr (assoc ...)), doing its 'if logic at expansion time. Here's a version that does that:

  (mac aget (al (o key nil))
    (if key            (= al (list al key))
        (isa al 'sym)  (= al (ssexpand al)))
    ; At this point, "al" means "arg list" rather than "assoc list." ;)
    (if (~and (alist al) (is 2 len.al)) (err "Invalid arguments to aget"))
    (let (al key) al
      `(cadr (assoc ,key ,al))))
And here's a light demonstration:

  arc> (= foo '((a 1) (b 2)))
  ((a 1) (b 2))
  arc> (aget foo 'b)
  2
  arc> (aget foo!b)
  2
  arc> (= (aget foo 'b) 3)
  3
  arc> foo
  ((a 1) (b 3))
  arc> (= (aget foo!b) 4)
  4
  arc> foo
  ((a 1) (b 4))

-----

3 points by rocketnia 2683 days ago | link

Whoops, at http://arclanguage.org/item?id=10788 aw brought up something I overlooked: adding new keys.

Here's an imperfect correction. It's the best I can think of without resorting to the power of hacking arc.arc and stuff.

  (mac aget (al (o key nil))
    (if key            (= al (list al key))
        (isa al 'sym)  (= al (ssexpand al)))
    ; At this point, "al" means "arg list" rather than "assoc list." ;)
    (if (~and (alist al) (is 2 len.al)) (err "Invalid arguments to aget"))
    (let (al key) al
      `(alref ,al ,key)))

  ; Because of technical difficulties (specifically, that al is already a value
  ; rather than a place at this point), this can't insert new entries at the
  ; beginning of al; they'd fall right back off, since the caller's variable (or
  ; other reference) is still bound to the "old" first spot, which is oblivious
  ; to anything before it. Instead, this works by inserting new entries at the
  ; *second* spot in al. On a related note, this can't modify anything about an
  ; empty assoc list, since an empty assoc list is nil, which is immutable.
  (defset alref (al key)
    (w/uniq (g-al g-key)
      `(,(list g-al al g-key key)
        (alref ,g-al ,g-key)
        (fn (val)
          (iflet entry (assoc ,g-key ,g-al)
            (= (cadr entry) val)
              ,g-al
            (push (list ,g-key val) (cdr ,g-al))
            (err "Can't set a value in an empty assoc list, sorry."))
          val))))

-----

2 points by aw 2683 days ago | link

This just gave me an idea... x!first translates into (x 'first), which for a list in arc3 expects an integer so that it can return the item in the list at that position. But this means that non-number arguments to calling a list is available: currently we get an error that the argument is not an integer. So if the argument isn't a number, calling a list could do an association list lookup instead. Thus:

  arc> x!first
  "First Name"
but

  arc> x!0
  (first "First Name")
as Arc does now.

The difference with your syntax is that with your syntax we could use integers as association list keys.

I'm interesting in this topic because in many places in my code where I'm using tables, I'm using tables for the convenience of the x!first syntax, not because I have a large number of keys and values that I'd need the efficiency of a hash table for.

-----