Arc Forumnew | comments | leaders | submit | rntz's commentslogin
5 points by rntz 5945 days ago | link | parent | on: Arc 3.1 (works on latest MzScheme)

I presume arc3.1 is only faster if you use plt4, which is attributable to plt4 being faster than mzscheme 372 thanks to improvements to the language implementation. The speed boost obviously doesn't come from making conses immutable, since Arc still has mutable conses.

If you're asking why immutable conses could speed up a programming language in general, well, a compiler can make more powerful optimizations if it's allowed to assume that lists won't mutate mid-flight. Obviously a specific program that used mutability of conses would need to be rewritten, and might end up faster or slower; but code that doesn't take advantage of the mutability of conses (of which there is a lot, Scheme having the functional heritage it does) can be speeded up.

Of course, given that Arc's mutable conses are accomplished by using pointer hacking to mutate plt4's supposedly immutable conses, one would hope the plt4 compiler doesn't take real advantage of the immutability of their conses, otherwise it might make assumptions that Arc will break, leading to difficult-to-detect bugs.

-----

1 point by herdrick 5943 days ago | link

Arc's mutable conses are accomplished by using pointer hacking to mutate plt4's supposedly immutable conses,

Really?

-----

5 points by rntz 5943 days ago | link

  ; Eli's code to modify mzscheme-4's immutable pairs.

  ;; to avoid a malloc on every call, reuse a single pointer, but make
  ;; it thread-local to avoid races
  (define ptr (make-thread-cell #f))
  (define (get-ptr)
    (or (thread-cell-ref ptr)
        (let ([p (malloc _scheme 1)]) (thread-cell-set! ptr p) p)))

  ;; set a pointer to the cons cell, then dereference it as a pointer,
  ;; and bang the new value in the given offset
  (define (set-ca/dr! offset who p x)
    (if (pair? p)
      (let ([p* (get-ptr)])
        (ptr-set! p* _scheme p)
        (ptr-set! (ptr-ref p* _pointer 0) _scheme offset x))
      (raise-type-error who "pair" p)))

  (define (n-set-car! p x) (set-ca/dr! 1 'set-car! p x))
  (define (n-set-cdr! p x) (set-ca/dr! 2 'set-cdr! p x))
Really. (This is an excerpt from arc3.1's ac.scm.)

-----

6 points by rntz 5946 days ago | link | parent | on: Arc 3.1 (works on latest MzScheme)

Diff available here: http://www.rntz.net/files/arc3.1.patch

It seems that plt4's immutable conses have been worked around via low-level pointer hacking. I can only hope this doesn't introduce subtle bugs, but given that Eli Barzilay seems to work on plt scheme, hopefully this is not the case.

I've pushed the changes to anarki's "official" branch.

-----


    brackify = lambda openb, closeb: lambda val: openb + val + closeb
The python code could also be shorter :).

-----


The multiple '=s are unnecessary:

    (= rbrackify (brackify "(" ")")
       cbrackify (brackify "{" "}")
       sbrackify (brackify "[" "]"))

-----

3 points by rntz 5952 days ago | link | parent | on: Noisy-each bug ?

This isn't a bug per se. It just prints the dot before it evaluates your code. So the first iteration, it prints 1; the second, 2; the third, 3; the fourth, it prints a dot and THEN prints 4.

-----

1 point by pg 5946 days ago | link

Yes.

-----

1 point by rntz 5953 days ago | link | parent | on: New ppr function

Another bug: 'indent-pairs can't handle being presented with a list containing only one element. For an example in the wild, see the source of 'rand-choice:

    arc> src.rand-choice
    (from "arc.arc")
    (mac rand-choice exprs
      `(case (rand ,(len exprs))
         Error: "car: expects argument of type <pair>; given 1"
More succinctly:

    arc> (ppr '(case foo ,@(bar baz quux xyzzy plugh very long)))
    (case foo
      Error: "car: expects argument of type <pair>; given 1"
The error lies in 'indent-pairs:

    (def indent-pairs (xs (o col 0))
      (let l (apply max (map len:tostring:print:car (keep [cdr _] pair.xs)))
        (on x pair.xs
            (if (~is index 0)
                (do (prn)
                    (sp col)))
            (let str (tostring:print car.x)
              (if cdr.x
                  (do pr.str
                      (sp:- l len.str -1)
                      (ppr cadr.x (+ col 1 l) t))
                  (do (sp (+ l 1)) ;offending expression
                      pr.str))))))
If 'xs is a list with only one element, then (pair xs) returns a list whose sole element is itself a list of one element. 'l is then the result of applying 'max to an empty list, which is nil. (+ l 1) then becomes (+ nil 1), which errors. One simple solution is to add a preceding 0 to the application of max.

    --- ppr-old.arc 2009-07-28 21:10:49.000000000 +0200
    +++ ppr.arc     2009-07-28 21:10:52.000000000 +0200
    @@ -54 +54 @@
    -  (let l (apply max (map len:tostring:print:car (keep [cdr _] pair.xs)))
    +  (let l (apply max 0 (map len:tostring:print:car (keep [cdr _] pair.xs)))

-----


The reason why the first example works is that `(foo (,a . ,b)) is syntax for (quasiquote (foo ((unquote a) unquote b))), which doesn't contain dotted lists.

I'll try my hand at fixing this when I have the time.

-----

2 points by rntz 5950 days ago | link

The fix I'm using for the time being is this:

    diff --git a/ac.scm b/ac.scm
    index 3304953..5263d28 100644
    --- a/ac.scm
    +++ b/ac.scm
    @@ -238,7 +238,7 @@
             ((and (pair? x) (eqv? (car x) 'quasiquote))
              (list 'quasiquote (ac-qq1 (+ level 1) (cadr x) env)))
             ((pair? x)
    -         (map (lambda (x) (ac-qq1 level x env)) x))
    +         (cons (ac-qq1 level (car x) env) (ac-qq1 level (cdr x) env)))
             (#t x)))

     ; (if) -> nil
This does weird things to quasiquote in medial position (see below), but IMO doing that is suspect, and it fixes both the "can't use dotted lists in quasiquoted expressions" bug and the "dotted unquote only works sometimes" bug:

    arc> `(foo . bar)
    (foo . bar)
    arc> `(foo . ,(join '(x) '(y)))
    (foo x y)
For those interested, more detail follows on the corner cases of quasiquotation in the underlying mzscheme. As it turns out, mzscheme (possibly schemes in general) has really weird quasiquoting rules. Consider the following:

    ; we begin with normal examples
    > (define x 'X)
    > (define X 'value)
    > `(a ,x)
    (a X)
    > `(a `(b ,,x))
    (a (quasiquote (b (unquote X))))
    
    ; quasiquoting in dotted position has interesting results:
    > `(a . `(b ,x))
    (a quasiquote (b (unquote x)))
    > `(a . `(b ,,x))
    (a quasiquote (b (unquote X)))
    > (eval `(list . `(b ,,x)))
    quasiquote: bad syntax in: quasiquote
    
    ; unquoting in terminal position results in splicing, as expected
    > '`(a . ,x)
    (quasiquote (a unquote x))
    > `(a . ,x)
    (a . X)
    > `(a . ,(list x))
    (a X)
    ; unquotes in medial positions error, which is good:
    > `(a unquote x X)
    stdin::1874: unquote: expects exactly one expression at: (#<syntax::1879> #<syntax::1887> #<syntax::1889>) in: (quasiquote (a unquote x X))
    
    ; however, the same is not true of quasiquotes in medial position.
    > `(a quasiquote b c)
    (a quasiquote b c)
    ; in fact, quasiquotes in medial position require increased unquoting of
    ; subsequent elements - quasiquoting in dotted position, above, is a special
    ; case of this
    > `(a quasiquote ,x ,x)
    (a quasiquote (unquote x) (unquote x))
    > `(a quasiquote ,,x)
    (a quasiquote (unquote X))
    ; however, if the list following quasiquote is dotted, this does not happen...
    > `(a quasiquote ,x . y)
    (a quasiquote X . y)
    ; ... UNLESS the dot comes immediately after the quasiquote
    > `(a quasiquote . ,,x)
    (a quasiquote unquote X)
Since arc quasiquotation compiles down into scheme quasiquotation, I'm unsure of the best way to handle all this at the arc level. What should all of these corner cases mean in arc? Moreover, consider that all uses of medial quasiquote (eg '(list quasiquote 2)) result in syntax errors when evaluated in scheme, but in arc, evaluating eg '(list quasiquote 2) is not a syntax error, and will depend upon the value of 'quasiquote; in fact, the same is true of most special forms:

    arc> (let quasiquote 0 (list quasiquote 1))
    (0 1)
    arc> (let assign 0 (list assign 1))
    (0 1)
Intuitively, binding values to special forms seems highly suspect, but there you have it.

-----

1 point by conanite 5954 days ago | link

Aha, I am enlightened, that explains why 'map doesn't complain.

It's still a mystery (to me) how ac manages to substitute unquote b because it's not a proper unquote. At some point, the x in

  (map (lambda (x) (ac-qq1 level x env)) x))
will be bound to the symbol 'unquote, so the compiler should never see (unquote b) ...

clearly, the only logical explanation is magic :)

-----

2 points by rntz 5953 days ago | link

The answer is that arc quasiquotation gets compiles down to scheme quasiquotation, and mzscheme's quasiquote handler understands (... unquote bar) to be equivalent to (... (unquote-splicing bar)). Since the arc compiler doesn't understand unquotes of this style, you'll only get the correct results when the expression unquoted is the same in arc and scheme - ie: it's either a literal, or it's a local variable. For example:

    ; this works because the local 'x in arc is compiled to 'x in scheme as well
    arc> (let x 0 `(a . ,x))
    (a . 0)
    ; this works because '+ in scheme adds numbers just as in arc
    arc> (let x 0 `(a . ,(+ x 1)))
    (a . 1)
    ; the following examples do not work
    arc> (let x '(b) `(a . ,(join x '(c))))
    Error: "reference to undefined identifier: join"
    arc> (let x '(b) `(a . ,(+ x '(c))))
    Error: "+: expects type <number> as 1st argument, given: (b . nil); other arguments were: (c)"
    arc> (= x '(b))
    (b)
    arc> `(a . ,x)
    Error: "reference to undefined identifier: x"
It's funny how a surface bug turns out to lead to something deeper in this way. I feel like Alice down the rabbit hole.

-----

2 points by rntz 5959 days ago | link | parent | on: Basic currying

I've never seen a convincing use case for currying macros; not sure why you bother with that. I find your use of 'args-key to enable reverse currying interesting - one wonders whether there are other possible uses. Personally I just use the following:

    (def curry (f . xs) (fn ys (apply f (join xs ys))))
    (def flip (f) (fn (x y . z) (apply f y x z))
As regards reversed currying and other more complex partial applications, I find the extended version of the [] syntax that I've cooked up for anarki's arc3.master branch to be quite nice - any symbol prefixed with an underscore is taken to be an argument to the function, and arguments are ordered alphabetically (with the special symbol '__ standing for the rest argument). So for example:

    arc> ([cons _2 _1] 'foo 'bar)
    (bar . foo)
    arc> ([list _ _a 0 __] 1 2 3 4)
    (1 2 0 (3 4))
In the first example, of course, [cons _2 _1] could be replaced with (flip cons).

-----

5 points by twilightsentry 5959 days ago | link

My motivation for currying macros came from the recent "afn with" discussion. I had a w/afn macro which looked like a manually-curried function, so I figured it wouldn't hurt to add it to the curry fn:

  (mac w/afn args
    `(w/rfn self ,@args))
vs.

  (= w/afn (>_ w/rfn 'self))
Your [] syntax definitely looks useful, but I use (fn ((x y))) more than (fn (x y)).

-----

3 points by rntz 5959 days ago | link

Huh. I hadn't thought of anaphoric macros (like afn and afnwith), but it's true: they're good examples of macro currying.

-----

1 point by rntz 5975 days ago | link | parent | on: New: inc, expanded range

I have (finally) merged and pushed these changes to anarki's arc3.* branches and hacktags.

I also have the beginning of a script for managing updating all these hacktags in parallel, but the sheer complexity of it is making me rethink the model of separating hacks from one another.

-----

1 point by CatDancer 5975 days ago | link

Yes, this parallels my experience that it was complex and difficult to use a version control system with the independent hack model.

-----

1 point by rntz 5974 days ago | link

What other choice is there if I want to maintain independence of hacks? If I just use plain diffs, how do I update my hacks when a new arc3.tar comes out and breaks them? Manually? That's even more of a pain.

-----

1 point by CatDancer 5974 days ago | link

I have some ideas that appear to be promising, but no solution to offer you yet. For example, I look at one of my patches and say, "why do I need a patch? Why isn't Arc hackable enough to let me implement this by just loading some code?" and, if I can, see if I can make Arc more hackable instead. And then, if it works, my patches become much smaller, just extending Arc to become more hackable instead of implementing my whole hack, small enough so that often the patch will still apply in new versions of Arc. Now when a new version of Arc comes out I don't always have to come up with a new patch, instead what I'm doing is simply testing my patches to see if they still apply.

-----

4 points by rntz 5975 days ago | link | parent | on: Bug in ++?

Not a bug in '++, a bug in the ssyntax expansion process:

    arc> (ssexpand '++.n)
    .n
    arc> (ssexpand '.n)
    (get n)
Infix + triggers ssyntax for ac-andf - even+odd expands to (andf even odd), for example. However, the expander doesn't handle +'s in non-medial position very well - it just cuts them out:

    arc> (ssexpand '++foo)
    foo
Since medial + is checked before medial dot in ssyntax expansion, this leads '++.n to expand to '.n and thence to (get n).

-----

4 points by rntz 5975 days ago | link

So it turns out '+ and '++ are actually special-cased in the ssyntax expansion process. From ac.scm:

    (define (ssyntax? x)
      (and (symbol? x)
           (not (or (eqv? x '+) (eqv? x '++) (eqv? x '_)))
           (let ((name (symbol->string x)))
             (has-ssyntax-char? name (- (string-length name) 1)))))
This is somewht ugly IMO, but it does mean that merely switching the order in which '. and '+ are ssexpanded fixes this particular bug:

    diff --git a/ac.scm b/ac.scm
    index 3304953..739a4f2 100644
    --- a/ac.scm
    +++ b/ac.scm
    @@ -89,9 +89,9 @@

     (define (expand-ssyntax sym)
       ((cond ((or (insym? #\: sym) (insym? #\~ sym)) expand-compose)
    +         ((or (insym? #\. sym) (insym? #\! sym)) expand-sexpr)
              ((insym? #\+ sym) expand-and)
          ;   ((insym? #\_ sym) expand-curry)
    -         ((or (insym? #\. sym) (insym? #\! sym)) expand-sexpr)
              (#t (error "Unknown ssyntax" sym)))
        sym))
Albeit it does change the semantics of some "correct" uses of ssyntax slightly (though not in a way I'd expect anyone to rely on); for example, 'foo+bar.baz expands to '(andf foo (bar baz)) under arc3, but expands to '((andf foo bar) baz) with this patch applied.

-----

3 points by pg 5973 days ago | link

Will fix.

-----

5 points by pg 5968 days ago | link

Looks like the answer is to use & instead of +. I like the look of + better, but I can tell people are going to want to use it in names.

-----

1 point by akkartik 5975 days ago | link

Ah, thanks. So this is a known problem?

-----

More