Arc Forumnew | comments | leaders | submitlogin
2 points by akkartik 4238 days ago | link | parent

Yes, I remember this aspect was quite subtle when I last poked at it a year ago, until rocketnia set me straight:

https://github.com/nex3/arc/commit/f311b3879c15a27518bbe0fad...

1. Arc 3.1 uses a kludgy uniq that doesn't actually guarantee unique symbols. I hooked it up with racket's infrastructure at some point, which happens to permit an optional arg.

  $ racket
  > (gensym)
  'g25
  > (gensym 'tmp)
  'tmp26
2. Also, anarki generalizes arc 3.1 by permitting any number of args to [], including zero:

  anarki> ([prn "abc" _1 _2] 34 35)
  "abc3435"
This is indeed incompatible with arc 3.1 in one way:

  arc3.1> ([prn "abc"] 34)  # Must provide one arg, but ok to not use it.
  "abc"
  anarki> ([prn "abc"] 34)  # Must provide exactly as many args as expected.
  Error: .. arity mismatch ..
Is this bad?


2 points by dido 4238 days ago | link

Well, how did the change not break setforms in arc.arc? Was the original definition for make-br-fn silently overwritten somewhere? Last I checked there is still a (let argsyms (map [uniq](cdr expr) ...) inside of the definition of setforms in arc.arc even in the latest versions of Anarki. This will not work with the change to bracket functions that has been made.

Frankly, I think that bracket functions should always have at least one argument permitted.

-----

3 points by rocketnia 4238 days ago | link

"Well, how did the change not break setforms in arc.arc? Was the original definition for make-br-fn silently overwritten somewhere?"

Yes. The [...] syntax is redefined in make-br-fn.arc after arc.arc is loaded.

I described my opinion of this at https://github.com/nex3/arc/commit/957501d81bafecab070268418.... It did break my code, but I don't mind breaking changes these days (maybe because I'm hardly a user!), and I think a:b syntax is already more useful than either version of [...].

-----

3 points by rocketnia 4238 days ago | link

"Frankly, I think that bracket functions should always have at least one argument permitted."

Groovy does this. The Closure syntax { ... } is shorthand for { it = null -> ... }, which is a one-argument Closure whose argument "it" defaults to null. When I moved from Groovy to Arc, I missed this.

-----

2 points by akkartik 4238 days ago | link

How do you feel about tossing out arity checking altogether in this instance? I could just add a rest arg so extra args never throw errors.

-----

2 points by dido 4237 days ago | link

Well, that would be helpful. After all the [...] syntax is meant to be a short-cut.

-----

2 points by akkartik 4236 days ago | link

Done: https://github.com/nex3/arc/commit/80571a42a6.

After some readability-golfing:

  (mac make-br-fn (body)
    (let args (rem '__ (dedup:sort > -mbf-argsyms.body))
      (if args
        (= (cdr lastcons.args) '__)
        (= args '__))
      `(fn ,args ,body)))
(https://github.com/nex3/arc/commit/a8a4008fed)

And after still more readability-golfing:

  (mac make-br-fn (body)
    (let args '__
      (each arg (dedup:sort > -mbf-argsyms.body)
        (pushnew arg args))
      `(fn ,args ,body)))akkartik/wart
(https://github.com/nex3/arc/blob/d03eed03f5/lib/make-br-fn.a...)

In the process, though, I've made some more backwards-incompatible changes. pushnew now behaves like push on non-lists or dotted lists.

  > (= x 3)
  > (push 2 3)
  (2 . 3)

  > (= x 3)
  > (pushnew 3 x)
  3
  > (pushnew 2 x)
  (2 . 3)
What do people think of this? Basically I want all list operations to treat atoms as degenerate dotted lists. Does this have any adverse implications for other aggregates (tables)?

All my changes, if you're curious: https://github.com/nex3/arc/compare/a8a4008fed...d03eed03f5

Update: As I expected, taking out the round-trip to table in the first version above saves us some consing. All the changes to get to the second version don't seem to result in any extra consing over the first.

  $ git checkout 80571a42a6
  $ racket -f as.scm
  arc> (load "cons.arc")  ; fallintothis's http://arclanguage.org/item?id=11122
  arc> ([+ _1 _2] 2 3)
  5
  arc> cons-count*
  20

  $ git checkout a8a4008fed
  $ racket -f as.scm
  arc> (load "cons.arc")
  arc> ([+ _1 _2] 2 3)
  5
  arc> cons-count*
  16

  $ git checkout d03eed03f5
  $ racket -f as.scm
  arc> (load "cons.arc")
  arc> ([+ _1 _2] 2 3)
  5
  arc> cons-count*
  16

-----

2 points by rocketnia 4236 days ago | link

"What do people think of this? Basically I want all list operations to treat atoms as degenerate dotted lists. Does this have any adverse implications for other aggregates (tables)?"

I don't see a problem, but I may not be the right person to ask. If it came down to it, I'd be willing to use 'table-pushnew, 'alist-pushnew, 'maxheap-pushnew, and so on. :-p

What other list operations do you have in mind? While 'pushnew makes sense to use with degenerate dotted lists, it's a very special case: It only clobbers the list at a finite depth, and that depth is actually zero. (Or zero in the unmodified list, anyway.)

-----

1 point by akkartik 4236 days ago | link

"What other list operations do you have in mind?"

Well, I've already changed reclist and some so things like this work:

  > (find 3 3)
  3
  > (find 3 '(2 . 3))
  3
  > (find 4 '(2 . 3))
  nil
More: https://github.com/nex3/arc/blob/76d078bcd0/arc.arc.t. I expect I've still missed some cases, and I'll keep tweaking them as I run into them until someone objects.

-----

3 points by rocketnia 4236 days ago | link

Oh, whoops. I forgot 'pushnew actually does traverse the input list to find out whether the element is new. I was thinking of 'push. XD

I disagree with the notion of membership you're using for dotted lists. If I 'pushnew nil onto (1 2 3 . nil), I want to get (nil 1 2 3 . nil), and it won't work that way if nil is already considered to be a member.

-----

1 point by akkartik 4236 days ago | link

Hmm, it seems to work:

  arc> (= x '(1 2 3))
  (1 2 3)
  arc> (pushnew nil x)
  (nil 1 2 3)

-----

2 points by rocketnia 4235 days ago | link

I think the membership check you're using is like this:

  (The final cdr is an element iff it isn't nil.)
  If the list is...
    A cons cell:
      If the car is the value we're looking for, succeed. Otherwise,
      continue by searching the cdr.
    Nil:
      Fail.
    A non-cons, non-nil value:
      Continue by comparing the list to the value we're looking for.
I feel we could simplify this specification by rewriting the last two cases in one of these ways, ordered from my least to most favorite:

  (The final cdr is an element.)
  (This breaks (pushnew nil ...) in existing Arc code.)
  ...
    A non-cons value:
      Continue by comparing the list to the value we're looking for.
  
  (The final cdr is not an element.)
  ...
    A non-cons value:
      Fail.
  
  (The final cdr is always nil and is not an element.)
  (Arc 3.1 already uses this.)
  ...
    Nil:
      Fail.
    A non-cons, non-nil value:
      Raise an exception.
By using the "element iff it isn't nil" approach, you're able to use 'pushnew to traverse the simple argument lists you build as intermediate results of that 'make-br-fn implementation. But I don't know if it's worthwhile to complicate the notion of "list" just to accommodate a special case of argument lists.

-----

1 point by akkartik 4234 days ago | link

Yeah, that's a valid summary of what I've done.

"I don't know if it's worthwhile to complicate the notion of "list" just to accommodate a special case of argument lists."

Yeah I see your point. My aim was to extend the correspondence between the syntax for vararg params, rest params and lists of regular params. But those features merely match a (proper) list of args to some template. I'm entangling the template with the notion of a list. Hmm..

-----