Arc Forumnew | comments | leaders | submit | cooldude127's commentslogin
3 points by cooldude127 6636 days ago | link | parent | on: Arc's defset : a little anti-arc?

Here is a macro called def= that will do basically what the equivalent CL defun would:

  (def fix-body (gens args body)
    (let genargs (map list args gens)
      (if (alist body)
          (map [fix-body gens args _] body)
          (find body args)
          (list 'unquote (alref genargs body))
          body)))

  (mac def= (name (val . args) . body)
    (let gens (map [uniq] args)
      (list 'defset name args
            (list 'w/uniq gens
                  (list 'list
                        (cons 'list
                              (flat (map list gens args)))
                        `(list ',name ,@gens)
                        (list 'quasiquote (join (list 'fn (list val))
                                                (fix-body gens args body))))))))
Here is an example definition for a caddr setter:

  (def= caddr (val xs)
    (= (car (cdr (cdr xs))) val))
Any improvements are welcome.

-----

4 points by cooldude127 6637 days ago | link | parent | on: A macrolet implementation

one suggestion: remove the outer set of parentheses to match a regular let. so do:

  (maclet (myassign x y) `(= ,x ,y)
    (let z 1
      (prn "z before: " z)
      (myassign z 42)
      (prn "z after: " z)
      z))
also, changed the name to maclet to match 'mac' for definitions. if necessary, also define a macwith like the regular with.

-----

2 points by almkglor 6637 days ago | link

As it is, macrolet is effectively a 'macwith.

-----

1 point by cooldude127 6636 days ago | link

yes i know, i would say this version should be macwith, and a version with less parentheses should be maclet. sorry if i wasn't clear.

-----

2 points by almkglor 6636 days ago | link

Yes I know too. Am currently thinking of overloading 'mac, such that:

  (mac:let (myassign x y) `(= ,x ,y)
    (let z 1
      (prn "z before: " z)
      (myassign z 42)
      (prn "z after: " z)
      z))
and:

  (mac:with
     (
       (myassign x y) `(= ,x ,y)
       (mysref s x i) `(= (,s ,i) ,x))
     (let ...))

-----

2 points by nlavine 6636 days ago | link

That is certainly the arc way to do it, but there's one thing I'm not sure about: do you want mac:let or let:mac? Or, in fact, would either one work? The problem I'm trying to get at is what happens to let's three arguments (symbol, value, body) when you compose mac with it. It seems like you really want mac to apply to only the value argument, which is not a straight composition.

I almost hate to get into this, but it seems to me that the real solution is first-class macros:

  (let my-macro (mac (x) `(prn ,x))
    ...)
I believe pg said that one of the things he gave up in the current implementation that he'd really like to have is first-class macros, and it may be that we can't get them in mzscheme without unacceptable performance loss, so perhaps maclet is the temporary solution, but this seems like the long-term way to go.

-----

2 points by absz 6636 days ago | link

How about

  (mac mc args
    `(annotate 'mac (fn ,@args)))
? Then

  arc> (= m (mc (x) `',x))
  #3(tagged mac #<procedure>)
  arc> (m not-a-declared-symbol)
  not-a-declared-symbol
. Of course, the downside here is that, as you observe, we can't do

  arc> (let m (mc (x) `',x) (m not-a-declared-symbol))
  Error: "reference to undefined identifier: _not-a-declared-symbol"
or

  arc> ((mc (x) `',x) not-a-declared-symbol)
  Error: "reference to undefined identifier: _not-a-declared-symbol"
. My understanding is that the problem with the second is that (annotate 'mac ...) is not evaluated before not-a-declared-symbol:

  arc> ((mc (x) `',x) 'not-a-declared-symbol)
  (quote not-a-declared-symbol)
. We get the same output in the other case:

  arc> (let m (mc (x) `',x) (m 'not-a-declared-symbol))
  (quote not-a-declared-symbol)
. However, here I'm not sure why. So after working through this, I'm not actually sure what these results signify. But here they are. Make of them what you will.

-----

1 point by cadaver 6636 days ago | link

There was a post a while ago about how you might get first-class macros through lazy compilation: http://arclanguage.com/item?id=842

I'm not exactly sure if this is what you want. I believe there is a difference between macros that are only expanded once (in lazy compilation), and macros that are called each time the macro-expression is encountered. Though the latter case would only make sense if the macro exhibits side-effects, or depends on side-effects, or if the argument list to the macro may change. Not sure whether there is any benefit in that.

-----

2 points by cooldude127 6636 days ago | link

first-class macros would obviously solve this problem completely, that same way scheme eliminates the need for CL's flet and labels. it would be nice if we could just assign anonymous macros to bindings, but we can't do that yet.

the problem is that the composition just doesn't really make sense in this context, as you said. it doesn't translate the way composition should. in my opinion, maclet is deserving of it's own form.

-----

1 point by almkglor 6636 days ago | link

No, what I mean is, to overload mac:

  (let oldmac mac
   (= mac
        (annotate
           (fn x
              (if (and (is 1 (len x)) (acons (car x)))
                 (my-code ...)
                 (apply oldmac x)))
           'macro)))

-----

2 points by cooldude127 6636 days ago | link

i'm not certain whether this would be abuse of function composition or not. for some reason, maclet and macwith just seem more appropriate than mac:let and mac:with. also seems sort of dangerous to override such an important construct.

-----

1 point by almkglor 6636 days ago | link

My concern is largely namespace pollution. By overloading (NOT overriding) 'mac, we squeeze this feature into a smaller namespace, adding 0 new macros ^^. In any case, the arc-wiki 'breakable and 'p-m macros have a similar intended use too ^^.

-----

1 point by nlavine 6636 days ago | link

It does seem like you're going to want to do a lot of macro composition where you only compose over the body argument. Is it possible to make this work? The most obvious way for me is to use the single-argument function syntax, but somehow modified for macros, like this:

([let x 3 _]:pm:body)

This is just a really quick thought. Any comments?

-----

1 point by almkglor 6636 days ago | link

Sorry, I don't understand your question. ~.~? !.! T.T

Internally, Arc transforms (foo:bar niz) to (foo (bar nitz)), so if 'foo is a macro, it will see an argument of (bar nitz)

-----

3 points by cooldude127 6637 days ago | link | parent | on: Modules : a proposition

i like the looks of this. i fully support taking advantage of the simplicity arc already has.

-----

1 point by cooldude127 6637 days ago | link | parent | on: First Class Special Forms?

how are they different now in any significant way?

-----

3 points by absz 6637 days ago | link

They're different because they don't actually exist. Observe:

  arc> def
  #3(tagged mac #<procedure>)
  arc> fn
  Error: "reference to undefined identifier: _fn"
Since def is a macro, we can use it anywhere we like. But since fn is not, we can only use it in the head of a list, and it can't be overridden. fn, if, etc. should be first class: if not macros, then (type fn) should be 'prim or some such thing.

-----

2 points by cooldude127 6637 days ago | link | parent | on: Arc's defset : a little anti-arc?

I think I will, when I have the time, write a macro in Arc to simplify this.

I'm surprised you say that writing a setf in CL is rare. Perhaps because usually CLOS is doing the work for you?

-----

1 point by kennytilton 6637 days ago | link

Oh, it is not just CLOS. defstruct also defines setters and many an apparent reader function is also a writer, (so we call them accessors). That means one does not fire up (defun (setf foo)...) unless there is an usual case of abstraction where there is some function on an object (not just reading a slot) and where there is some state change intelligibly viewed as being transitive aka share the name with the reader.

In re writing a defun, I probably just talked you out of it, but here is what I did (warning, old nooby Arc code) for optional and keyword args:

  (mac defun (name params . body)
   (w/uniq (rtargs)
    `(def ,name ,rtargs
       (withs ,(with (reqs nil key? nil opt? nil keys nil opts nil without)
                (each p params
                  (if (is p '&o) (do (assert (no opt?) "Duplicate &o:" ',params)
                                     (assert (no key?) "&k cannot precede &o:" ',params)
                                   (= opt? t))
                    (is p '&k) (do (assert (no key?) "Duplicate &k:" ',params)
                                   (= key? t))
                    key? (push-end p keys)
                    opt? (push-end p opts)
                    (do (assert (~acons p) "Reqd parameters need not be defaulted:" p)
                        (push-end p reqs))))
                (with (n -1 kvs (uniq))
                  (+ (mappend [list _ `(nth ,(++ n) ,rtargs)] reqs)
                    (mappend [list (carif _) `(or (nth ,(++ n) ,rtargs)
                                                ,(cadrif _))] opts)
                    (list kvs `(pair (nthcdr ,(++ n) ,rtargs)))
                    (mappend [list (carif _)
                               `(or (alref ,kvs ',(carif _))
                                       ,(cadrif _))] keys)
                    )))
         ,@body))))

-----

7 points by cooldude127 6637 days ago | link

also, i find myself not using clos or defstruct all that much in my cl code. i tend to use ordinary data structures like lists and arrays.

my recent example (an excerpt from which i already showed) was a sudoku solver. i wrote one version in CL, then thought i'd do it in arc for fun. in CL, the puzzles were simply arrays (in Arc, they were lists do to the lack of an array type).

However, i didn't want my code to use the array (or list) accessors built into the language to access values in the puzzle, because that is not abstract enough. so i defined a function called pval that takes the puzzle and an x and y, giving the value in that square. that way, my code deals with puzzles, not arrays.

Then I wanted to be able to set the value in a square using the same pval accessor. Trivial in CL, but Arc made it a little difficult. This comes up rather often for me.

That point might not have justified such a long explanation. Whoops.

-----

2 points by cooldude127 6637 days ago | link

all i really intend to do is a simple def= macro that will transform (def= pval (val puz x y) (= ((puz y) x) val)) into the previously given defset form. shouldn't be too bad. i have no intentions of mimicing CL's defun. i honestly like arc's def as it is.

-----

1 point by cooldude127 6638 days ago | link | parent | on: NewLISP anyone?

those sound like a lot of things i want. sorry newlisp

-----

2 points by cooldude127 6639 days ago | link | parent | on: symbol macros, please?

or maybe mr. graham will consider cl's iterate package, which is more sane and lispy than loop.

-----

3 points by kennytilton 6639 days ago | link

The weird thing is that loop seems Arcy-er than iterate cuz it like Arc goes out of its way to minimize parentheses. Worth the learning curve, I assure you.

-----

2 points by vsingh 6638 days ago | link

Didn't Paul call Loop one of the worst mistakes in Common Lisp? I doubt the man is going to change his mind on this one.

Loop may minimize parentheses (i.e. nesting), but then again, so does BASIC. I'd say the main reason it isn't "Arcy" is that it fails the simplicity-of-implementation test.

-----

2 points by kennytilton 6638 days ago | link

I think you misunderstand. The "weird" to which I refer is precisely pg's anti-(cl)-loopism. He should love loop not just for the brevity, but also because it is a triumph of DSL. ie, CL loop is a DSL for iteration, and I know because I use it for everything. But! pg seems closer to being a schemer at heart so maybe he prefers recursion for things I handle by iteration.

ps. Are you saying BASIC eliminates parentheses the same way LOOP does? :)

-----

1 point by vsingh 6638 days ago | link

It's hard for me to see Loop as a triumph of DSL. I see it more as a failure of Common Lisp to provide simple flexible iteration constructs.

Arc already seems to provide simple operators for many of the cases in which Loop is commonly used. For example: 'accum', 'for', and 'repeat'. It doesn't seem like we need Loop.

Re BASIC: In a way, yes. Both BASIC and Loop use keywords to replace the indication of structure by parentheses. For example, we could eliminate a pair of brackets in 'with' by doing this:

    (with x 2 y 3 z 4 endvars
        (+ x y z))
but that wouldn't be very Lispy.

-----

2 points by kennytilton 6638 days ago | link

Well I resisted Loop for almost ten years then broke down and learned it when PCL came out cuz it had a good chapter on it, so having made the transition I can assure you it is a powerful little iteration language, not just a simple iterator of which CL has many so it is not clear what you do not like about those. I'll ignore your continued insistence that BASIC has anything to do with this discussion. :)

Btw, loop offers a little-known second syntax that is Lispy, one just never sees examples in the wild.

-----

2 points by vsingh 6638 days ago | link

If you have found through long experience that Loop truly offers something unique that can't be offered as well (or as intelligibly) by a combination of simpler operators, then I'll have to take your opinion seriously.

I've made a thread to collect exemplary examples of Loop in action (http://arclanguage.org/item?id=2938) and it'd be great if you could contribute your favorites, e.g. from Cells or other code you've written.

Personally, I don't have the same experience as you. I've found Loop to be useful as a replacement for iteration constructs that should have been in CL to begin with, e.g. dovector, when I'm too lazy to code the requisite macro. I haven't ever had to use Loop in its full complexity.

-----

4 points by kennytilton 6638 days ago | link

Sorry, it just sounds from what you have posted so far that you do not know CL's loop, by which I mean make an effort to use all of its capabilities such that you would know what's there so well that you had it at your fingertips. I am having the same problem with CL idiots denouncing Arc who probably have not even installed it.

You want me to code (dotimes (x 10) (foo))?! No way!

  (loop repeat 10 do (foo)). 
And that is not even a good example!!

  (let (x)
     (dolist (y whatever (nreverse x))
        (let ((py (pfft y)))
           (when py
               (push (yo-mama (cons y py)) x)))
Egad! How about:

   (loop for y in whatever
         for py = (pfft y)
         when py collect (yo-mama (cons y py)))
Is the first version starting to look like a disassembly? Bingo!!! :)

btw, the thing that got me to break down and give loop a chance was learning that the expansion was highly optimized code, meaning (for example) it would not first map across whatever and then delete the nils.

but these examples still do not get to the point of LOOP being a DSL. That property emerges only in the next level of application, as even the loop form expands to ten lines. But once you have loop under your belt (I kill myself) you do not even want to code a simple dotimes, that bogus unused count variable just pisses you off no end, never mind all the extra (wait for it) parentheses!

pg, phone home, all is forgiven! :)

-----

2 points by vsingh 6638 days ago | link

That looks interesting. I've submitted that example to my thread.

-----

1 point by almkglor 6639 days ago | link

/me loves 'loop

-----

6 points by cooldude127 6640 days ago | link | parent | on: Syntax for optional/keyword args

it's a good idea, but i'm not sure i'm big on borrowing the unix syntax. it just looks kinda ugly and unlispy to me.

-----


I love the idea of wrapmac!

-----

4 points by cooldude127 6644 days ago | link | parent | on: Another, older wannabe neo-Lisp

partial application is something I always miss in lisp. I often find myself writing anonymous functions that would be much shorter with partial application. here are a few examples from some of my own CL code (pay attention to the second let binding):

With anonymous function

  (defmethod do-step ((w world))
    (let* ((locs-to-check (all-cells w))
           (actions (mapcar (lambda (x) (act w x)) locs-to-check)))
      ...)
With an explicit curry macro:

  (defmethod do-step ((w world))
    (let* ((locs-to-check (all-cells w))
           (actions (mapcar (curry act w) locs-to-check)))
      ...)
The way it should be (built into the language):

  (defmethod do-step ((w world))
    (let* ((locs-to-check (all-cells w))
           (actions (mapcar (act w) locs-to-check)))
      ...)
It might seem small, but do it enough times and you appreciate the succinctness.

-----

4 points by raymyers 6643 days ago | link

I also enjoy currying and "pointfree" style. However, wouldn't it require giving up functions that take an arbitrary number of arguments? (In fact Scala has both, but it required introducing a special syntax.)

I find that Arc's shortcut closure [] notation is a surprisingly good curry-substitute. For example:

    (map (act w) locs-to-check) ; currying
    (map [act w _] locs-to-check) ; only one token longer

-----

2 points by cooldude127 6642 days ago | link

there's something to that. i didn't really think about it, but that might just be fine. that's actually exactly what scala's explicit partial application looks like, with the underscore.

i honestly haven't been doing a whole lot of practical arc programming. CL and Slime have spoiled me to the point where I find it difficult to use a language that doesn't have that convenience

-----

More