Arc Forumnew | comments | leaders | submit | pg's commentslogin
3 points by pg 5845 days ago | link | parent | on: Request for Bugs

It wasn't intended to, but I'd considering broadening the def. I'll look at the hn source to see how often I'd use this.

-----

1 point by thaddeus 5844 days ago | link

As a note - It may still be a good idea, but I care less after some thought:

Originally I was trying grab the last number of items in a table (something similar to your code in prompt.arc where you are grabbing/printing the last n items passed into the repl).

When using a list, as you had, we can easily grab the last 'n' items in the list since lists maintain order.

    (= records* (list "record 1 text" "record 2 text" "record 3 text" "record 4 text" "record 5 text" "record 6 text"))

    (def lastn (n nlist)
      (firstn n (rev nlist)))

    arc>(each record (lastn 3 records*)
      (prn record)) 

    record 6 text
    record 5 text
    record 4 text
However if storing the data in a table it's not as clear grabbing the last n items since tables have no inherent order. as you can see this isn't as straight forward:

    (= records* (obj 1 "record 1 text" 2 "record 2 text" 3 "record 3 text" 4 "record 4 text" 5 "record 5 text" 6 "record 6 text"))

    arc> (each record (firstn 3 (sort > (keys records*)))
         (prn (records* record)))

    record 6 text
    record 5 text
    record 4 text
so I had hoped 'for' in reverse would be cleaner...

    arc> (for i (len records*)(- (len records*) 3)
          (prn (records* i)))

    record 6 text
    record 5 text
    record 4 text
    record 3 text
only after re-writing I noticed that I was getting too many results thus ended up having to hack:

    arc> (for i (len records*)(- (len records*) 2) 0
          (prn (records* i)))
If my hope was to make the code more readable, it didn't work.

So.... instead I wrote 'grab':

    (def lastn (n nlist)
      (firstn n (rev nlist)))

    (def grab (which n them (o sop nil))
	 (case (type them)
	        table (case which first (if (no sop) (firstn n (sort < (keys them)))(firstn n (sort sop (keys them))))
	                          last  (if (no sop) (firstn n (sort > (keys them)))(firstn n (sort sop (keys them)))))
	        cons  (case which first (if (no sop) (firstn n them)(firstn n (sort sop them))) 
	                          last  (if (no sop) (lastn n them) (lastn n (sort sop them))))))
	
And now I feel it's much more readable + works on both lists and tables:

    arc>(each record (grab 'last 3 records*)
        (prn (records* record)))

    record 6 text
    record 5 text
    record 4 text
T.

[EDIT] - Hadn't thought about it until now, but it has re-use even if the table doesn't have nums for keys, it would also work on other keys (example grabbing the last n items of alphabetical order). Anyway... don't spend time on re-working 'for' it it doesn't help anyone else.....

-----

5 points by pg 5845 days ago | link | parent | on: Request for Bugs

I care because to do a good job at language design you have to use the language to write applications.

-----

4 points by pg 5845 days ago | link | parent | on: Request for Bugs

Good idea; fixed:

    (def memo (f)
      (with (cache (table) nilcache (table))
        (fn args 
          (or (cache args)
              (and (no (nilcache args))
                   (aif (apply f args)
                        (= (cache args) it)
                        (do (assert (nilcache args))
                            nil)))))))

-----

3 points by pg 5845 days ago | link | parent | on: Request for Bugs

It's intentional. You have to use an explicit rev if you care about the order-- since sometimes you might not. But I'm open to changing that, because I'm often annoyed to have to add the explicit rev. (This is why you have to write apps in a language at the same time you're working on it.)

-----

2 points by CatDancer 5845 days ago | link

I use rev:accum often enough that I'd use an operator that did that (whether it was called accum or something else).

-----

5 points by pg 5844 days ago | link

Ok, I just changed accum to do the rev.

-----

1 point by thaddeus 5844 days ago | link

thanks. T.

-----

3 points by pg 5845 days ago | link | parent | on: Request for Bugs

Oops.

    (def intersperse (x ys)
      (and ys (cons (car ys)
                    (mappend [list x _] (cdr ys)))))

-----

1 point by pg 5845 days ago | link | parent | on: Request for Bugs

Thanks; this is now fixed.

-----

12 points by pg 5847 days ago | link | parent | on: Request for Bugs

Ok, this is done.

    (define (expand-sexpr sym)
      (build-sexpr (reverse (tokens (lambda (c) (or (eqv? c #\.) (eqv? c #\!)))
                                    (symbol->chars sym)
                                    '()
                                    '()
                                    #t))
                   sym))

    (define (build-sexpr toks orig)
      (cond ((null? toks)
             'get)
            ((null? (cdr toks))
             (chars->value (car toks)))
            (#t
             (list (build-sexpr (cddr toks) orig)
                   (if (eqv? (cadr toks) #\!)
                       (list 'quote (chars->value (car toks)))
                       (if (or (eqv? (car toks) #\.) (eqv? (car toks) #\!))
                           (err "Bad ssyntax" orig)
                           (chars->value (car toks))))))))

    (define (tokens test source token acc keepsep?)
      (cond ((null? source)
             (reverse (cons (reverse token) acc)))
            ((test (car source))
             (tokens test
                     (cdr source)
                     '()
                     (let ((rec (if (null? token)
                                acc
                                (cons (reverse token) acc))))
                       (if keepsep?
                           (cons (car source) rec)
                           rec))
                     keepsep?))
            (#t
             (tokens test
                     (cdr source)
                     (cons (car source) token)
                     acc
                     keepsep?))))
get, incidentally, is a new operator defined thus:

    (def get (index) [_ index])
so

    arc> (map !a (list (obj a 1) (obj a 2)))
    (1 2)

-----

4 points by pg 5848 days ago | link | parent | on: Request for Bugs

Looks like the first can be fixed thus:

    (def expand-metafn-call (f args)
      (if (is (car f) 'compose)
           ((afn (fs)
              (if (caris (car fs) 'compose)            ; nested compose
                   (self (join (cdr (car fs)) (cdr fs)))
                  (cdr fs)
                   (list (car fs) (self (cdr fs)))
                  (cons (car fs) args)))
            (cdr f))
          (is (car f) 'no)
           (err "Can't invert " (cons f args))
           (cons f args)))

-----

5 points by pg 5857 days ago | link | parent | on: Request for Bugs

One problem with the newer versions is that they made cons cells immutable. Is there a way to turn that off?

-----

3 points by elibarzilay 5847 days ago | link

First of all, updating to the recent plt version will be good, since there have been a lot of optimizations since then, including a lot of work to the compiler and the jitter.

Arc needs to do some list marshaling in any case to deal with nil -- and as such it reimplements nearly all of the list operations, deferring to mzscheme functions only for a few things. Because of this, using `mcons' shouldn't be too problematic (not even for speed, since arc functions in general are not careful about cyclic lists, a plain implementation will be fast enough -- for example, mzscheme's `map' is now implemented in scheme). I have done some tests in the past to measure

Probably the only major problem is how to deal with a rest argument. One option is to make it illegal to mutate rest arguments. Another is to hook on the fact that rest arguments are not modified often -- so instead of copying them to a mutable list on every call, they can be converted only when needed, by the functions that change the contents of these lists. Given the above (need to implement list functionality anyway), this wouldn't be difficult -- basically some wrapper around a mzscheme list that can do the necessary work. As a side benefit it can be used to deal with nil too, which will make that run faster as well.

(BTW, this will be much faster than using r6rs or r5rs modes.)

-----

1 point by CatDancer 5847 days ago | link

One option is to make it illegal to mutate rest arguments.

Currently, in arc.arc, list is defined as

  (def list args args)
so this would need to change if rest arguments couldn't be mutated. (Which would be OK with me, I can't think of a single case where I ever mutated a rest argument).

Another is to hook on the fact that rest arguments are not modified often -- ... they can be converted only when needed

How could this possibly work??

  ((fn args
     (= (cadr args) 'X)
     args)
   'a 'b 'c)
sure, it would be easy for set-cdr! to see that the second pair is immutable and decide to create a new mutable cons. But args is still going to be pointing to the original list of immutable pairs!

As a side benefit it can be used to deal with nil too, which will make that run faster as well.

Can you explain? (I need smaller steps to be able to follow you :-)

(BTW, this will be much faster than using r6rs or r5rs modes.)

Yes, I was getting that impression browsing through the r6rs and r5rs code.

The way I'm leaning right now is to first rewrite the Arc compiler to generate PLT 4 code in the simplest possible way, for example to always convert rest arguments to mutable lists. Then, things like making rest arguments immutable could be done as an optimization if desired.

-----

1 point by elibarzilay 5846 days ago | link

For the "on-demand" conversion some PLT magic will be needed -- I'm basically talking about doing something at the level of the PLT function that is the result of compiling an arc function. In any case, explaining more through this medium will be hard for me...

-----

1 point by CatDancer 5846 days ago | link

Send me an email at cat@catdancer.ws, or use a pastebin and post the link here.

-----

4 points by herdrick 5857 days ago | link

Nope. All you can do is use mcons ('mutable cons'), mcar, mcdr, etc. But apparently there are only a few functions that work with these things. It's like using lists in a language that doesn't support lists well. Useless.

  > (require scheme/mpair)
  > (require (lib "1.ss" "srfi"))
  > (lset-difference eq? (list 1 2) (list 2 3))
  (1)
  > (lset-difference eq? (mlist 1 2) (mlist 2 3))
  {1 2}
This really burns me.

-----

3 points by CatDancer 5855 days ago | link

Hmmmmmm... I had thought that r6rs compatibility mode wasn't very useful, if it just made car a synonym for mcar etc. But it does more, for example unlike in regular plt-4 where in (lambda args ...) args is an immutable list, in r6rs mode:

  #!r6rs
  (import (rnrs) (rnrs mutable-pairs (6)))
  (define x ((lambda a a) 'a 'b 'c))
  (set-car! (cdr x) 'd)
  (write x)
  (newline)

  $ plt-4.1.5/bin/mzscheme -t a.scm
  (a d c)
This looks like it could solve a lot of problems with a port to plt-4, since otherwise we'd need to be rewriting the Arc compiler to change the expansion of (fn args ...) etc.

-----

2 points by CatDancer 5853 days ago | link

Played around with it a bit more, r6rs appears problematic as apparently ++ is not a legal symbol in r6rs (!)

I took a look at PLT's implementation of lambda for r6rs/r5rs (it's in collects/r5rs/main.ss), and they just simply convert to a mutable list if the lambda has a rest parameter:

  (define-syntax (r5rs:lambda stx)
    ;; Convert rest-arg list to mlist, and use r5rs:body:
    (syntax-case stx ()
      [(_ (id ...) . body)
       (syntax/loc stx (#%plain-lambda (id ...) (r5rs:body . body)))]
      [(_ (id ... . rest) . body)
       (syntax/loc stx
         (#%plain-lambda (id ... . rest)
                         (let ([rest (list->mlist rest)])
                           (r5rs:body . body))))]))
(the list->mlist is the part I'm looking at)

So having for example (fn args ...) compile to an (arc-lambda args ...) which does the same thing might be simpler than trying to get Arc to compile and run in the whole complicated r6rs environment.

-----

4 points by pg 5857 days ago | link

Incidentally, does the (or some) Scheme standard allow an implementation to make cons cells immutable? I.e. is supporting set-car! optional?

-----

5 points by CatDancer 5857 days ago | link

Strongly deprecated:

http://www.r6rs.org/final/html/r6rs-rationale/r6rs-rationale...

I participated a bit in the discussion while r6rs was being created, but it quickly became apparent to me that the goals that the editors were striving for weren't things that I personally cared about.

So, anyway, due to the "compromise", can we get mutable pairs if we run MzScheme in r6rs compatibility mode?

a.scm:

  #!r6rs
  (import (rnrs) (rnrs mutable-pairs (6)))

  (define x (cons 'a 'b))
  (set-car! x 'c)
  (write x)
  (newline)

  $ ./mzscheme a.scm
  (c . b)
So yes, though it turns out that in r6rs compatibility mode cons is really just mcons, so we don't actually gain anything.

-----

1 point by herdrick 5857 days ago | link

Interesting. Have you tried r6rs compat mode?

It looks like SRFI 1, at least, isn't working with r6rs: http://list.cs.brown.edu/pipermail/plt-dev/2009-March/000283...

-----

2 points by CatDancer 5856 days ago | link

Have you tried r6rs compat mode?

Not any more than I've shared here.

-----

1 point by lojic 5851 days ago | link

Some info from: http://docs.plt-scheme.org/release-notes/mzscheme/MzScheme_4...

  Indeed, our experience is that making the result of `cons' immutable
  does not create many porting problems. Nevertheless, if your code does
  use `set-car!' or `set-cdr!', and if converting to a more functional
  style is difficult, then consider using `mcons' and the `scheme/mpair'
  library.

-----

9 points by pg 5945 days ago | link | parent | on: Arc presentation

I don't know what exactly agile developers would care about, but the guiding principle of Arc is to make programs short. Partly because that's what programming languages are for, but also because programs are meant to be changed, and shorter programs are easier to change. The latter part sounds like it should be relevant to them.

-----

2 points by conanite 5943 days ago | link

Totally. "Embrace Change" was the mantra of the first book on XP. And to paraphrase "maximizing the amount of work not done" (thanks, CatDancer, for the link) we could say "maximizing the number of lines not written".

The usual techniques for enabling change involve exhaustive tests as well as not implementing stuff that might be unrequired. And DSLs. Hence Ruby gets a lot of attention because it's so easy to build a quick DSL in it. But as you've mentioned elsewhere, a layer of lisp macros in a program is effectively the DSL for that system.

Possibly the advantage of lisp in the context of DSLs is that the use of macros is thoroughly studied, and in a mature lisp the usual editing, debugging and optimization tools are available, whereas a home-made DSL might create as many problems as it solves ( ad-hoc, informally specified, bug-ridden, etc ).

-----

More