Arc Forumnew | comments | leaders | submit | evanrmurphy's commentslogin
1 point by evanrmurphy 5523 days ago | link | parent | on: Macro question

Are there keywords besides in that you're hoping to use with foreach, or did you just want in so that it reads more like English?

-----

1 point by d0m 5523 days ago | link

In this case, it was only in so it reads more like english. But, I was interested in the more general idea about how to add keyword in macro.

-----

4 points by evanrmurphy 5523 days ago | link | parent | on: Macro question

I know I can do it with: (foreach x 'in seq) and use (is 2nd-arg 'in).

This is probably the most intuitive approach, but since foreach is a macro, you don't need to quote in.

  arc> (mac foreach (var in expr . body)
         `(each ,var ,expr
            ,@body))
  #(tagged mac #<procedure: foreach>)

  arc> (foreach x in '(a b c)                ; in, not 'in
         (prn x))
  a
  b
  c
  nil
My definition is silly because it doesn't do anything with the in arg. It doesn't even require that the arg be in!

  arc> (foreach x fruitloops '(o o o)
         (prn "yum"))
  yum
  yum
  yum
  nil
More interesting things are certainly possible, though. :)

-----

2 points by d0m 5523 days ago | link

Oh wow, thank you. For some reasons, I thought I couldn't just write (var in expr. body) since in the call:

(foreach x in '(..)), "in" would have been evaluated to an unknown symbol.. However, I was totally wrong since I don't even have to evaluate it! :)

-----

1 point by d0m 5523 days ago | link

Let's suppose however that I want to check if it is really in and not fruitloops.. Is there a way?

For example:

(foreach x in seq (prn x)) -> call (each x seq (prn x))

(foreach x seq (prn x)) -> call (each x seq (prn x))

(foreach x fruitloops seq (prn x)) -> (prn "mmmm")

-----

1 point by evanrmurphy 5523 days ago | link

It's not pretty, but you could do:

  (mac foreach (var . args)
    (if (is car.args 'in)
          `(each ,var ,(cadr args)
             ,@(cddr args))
         (is car.args 'fruitloops)
          `(each ,var ,(cadr args)
             (prn "yum"))
         (acons car.args)
           `(each ,var ,(car args)
              ,@(cdr args))))
Update: I'm not sure it's any more clear, but another option would be,

  (mac foreach (var . args)
    `(each ,var ,(if (in car.args 'in 'fruitloops)
                      cadr.args
                     car.args)
       ,@(case car.args
           'in         cddr.args
           'fruitloops `((prn "yum"))
                       cdr.args)))
except that it's not working correctly for the 'fruitloops case and I can't figure out why.

Upperdate: Aha! It's because in and fruitloops in the case statement should not be quoted:

  (mac foreach (var . args)
    `(each ,var ,(if (in car.args 'in 'fruitloops)
                      cadr.args
                     car.args)
       ,@(case car.args
           in         cddr.args                       ; in not quoted
           fruitloops `((prn "yum"))                  ; ditto fruitloops
                      cdr.args)))
This works.

-----

1 point by akkartik 5523 days ago | link

The convention I use is to begin keywords in function calls with a colon.

  (mac foreach (var :in expr . body)
    ..

-----

1 point by d0m 5523 days ago | link

Doesn't it make a little bit less pretty?

(foreach x in '(1 2 3)) vs (foreach x :in '(1 2 3)) ?

I'm sure you have good reasons to use this convention.. mind sharing them?

-----

2 points by evanrmurphy 5523 days ago | link

I think the colon is his convention for within the definition, as a visual marker to distinguish keyword parameters from others. When you later call a function or macro defined as such, you won't need to prepend the colon to arg you're passing (similar to how mine was called in within the definition but could be fruitloops when I called it).

Edit: On second thought, probably does use the colon when calling the function as well.

-----

3 points by shader 5522 days ago | link

The colon is a standby from other lisps; Common Lisp would intern symbols starting with : to a special KEYWORD symbol table.

That was important because you could do things like keyword based arguments to functions, etc. i.e. if you had a function that was defined as

  (defun greet (&key (a "Hello") (b "World")) 
    (print a " " b))
then you could call it like:

  >(greet)
  Hello World
  >(greet :b "Bob")
  Hello Bob
  >(greet :b "Alice" :a "Yo")
  Yo Alice
etc. As you can see, it's a lot more flexible than "traditional" optional arguments, since you can specify any of them, and in any order.

-----

1 point by akkartik 5522 days ago | link

Yes I've been using the : in calls as well, but as I wrote the comment it occurred to me that I didn't have to. Either works.

-----

2 points by akkartik 5522 days ago | link

The prettiness wasn't a concern; as programmers we're used to needing hyphens or underscores, to ignoring parentheses and focusing on indentation. The colon's like that.

-----

1 point by evanrmurphy 5524 days ago | link | parent | on: Try Arc->Javascript

Could you help me to better understand the metafn issue? Those (do:a b) examples... what should they compile to? Does what you're talking about affect semantics, or just readability and performance?

-----

2 points by rocketnia 5524 days ago | link

When Arc compiles (a:b c), it compiles ((compose a b) c). When it compiles ((compose a b) c), it compiles (a (b c)). So in Arc, (do:a b) and (do (a b)) are basically equivalent, and so are (a:do b) and (a (do b)), where 'do is just an example macro. Since 'do is a macro, the equivalence does involve semantics; your transformation of (a:do b) calls a function named "do", when that function may not actually be defined.

Anyway, I'm just trying to make sure I inform you of stuff you may have overlooked. Once you know what I'm talking about regarding 'compose, it's up to you to decide whether you actually want to preserve an (a:b c)-(a (b c)) equivalence. ^_^ I am quite a fan of it, 'cause it saves some nontrivial edits (adding a closing parenthesis in a distant place and reindenting), but it's not sacred or anything.

-----

1 point by evanrmurphy 5524 days ago | link

Ah, I think I understand now. I had seen this comment from arc.arc:

  ; Composes in functional position are transformed away by ac.
And your examples show how 'compose gets transformed away, but I was having trouble visualizing a case where that wouldn't happen. Now it seems obvious to me: if you have (compose a b), rather than ((compose a b) c), then you can't transform compose away, because something is needed to make a and b act like one function.

-----

1 point by evanrmurphy 5525 days ago | link | parent | on: Try Arc->Javascript

Sorry about the lack of string escaping (that's awfully annoying! ^_^). It should be working now, at least for the common cases:

  "'"   ->  '\'';
  "\'"  ->  '\'';
Thanks for the input. I'm studying the metafn problem.

Edit: Actually backslash chars appear to escape incorrectly still. It's doing "\\'" -> '\\'' instead of "\\'" -> '\\\''. Working on it... Update: fixed.

-----

1 point by rocketnia 5524 days ago | link

Ack, there's something missing from that implementation: Nested string handling.

This is working well:

  "\\"
  '\\';
If you use 'quote to compile a string without escapes, you're fine:

  '"foo"
  '\'foo\'';
If you do the same thing where the only things to escape are quotes, you're fine, 'cause you call 'js-q, which uses 'nest-lev:

  '"'"
  '\'\\\'\'';
However, with other escaped things you're in trouble:

  '"\\"
  '\'\\\'';
I suggest putting 'js-charesc where 'js-q is now and having it use 'nest-lev directly. You might not even need 'js-q, since it could just be (js-charesc #\').

-----

1 point by evanrmurphy 5524 days ago | link

I'm not sure that's incorrect. Are you suggesting it should compile to this?

  '\'\\\\\'';
In Chrome's Javascript console, '\\'; and '\'\\\''; evaluate the same except for the extra pair of quotes.

-----

2 points by rocketnia 5524 days ago | link

Right. I'm going to use [] as makeshift quotes. The JavaScript ['\'\\\\\''] evaluates to ['\\'], which evaluates to [\]. The JavaScript ['\'\\\''] evaluates to ['\'], which evaluates to an error, 'cause it's an unterminated string.

-----

3 points by evanrmurphy 5524 days ago | link

OK. Now I'm going to use [] to illustrate how the Walls of Shame closed in on me as I read your last comment:

  [     :)     ]
    [   :|   ]
      [ :-o ]
        [:(]
You were right. ^_^

-----

2 points by evanrmurphy 5525 days ago | link | parent | on: Libraries support

> First, sorry for my newbie question.

No need to apologize. :)

> stuck because I don't have external libraries (smart time parser, sockets, mysql, etc.)

Well, I would consider html.arc, srv.arc, app.arc, strings.arc and even some of arc.arc to be libraries. And if you include Anarki's lib/ directory along with all the resources from places like http://awwx.ws/, http://github.com/rocketnia/lathe and this forum, I think we've got a sizable collection going. Granted a lot of it is scattered and not as developed as in some other languages, and we also don't have a standard module system yet. :P

> Is that what PG had in mind when he created Arc? To supply a minimal language but to be able to use external libraries in the underlying interpreter..?

You might be interested (if you haven't already read them) in what pg has written about arc at http://www.paulgraham.com/arc.html. My impression is he thinks it's too soon to get focused on libraries because he's still working on the core language. (http://arclanguage.org/item?id=12127 is a relevant exchange too.)

> a little bit more like I would use Django? I.e. a simple routing to functions and a built-in template engine instead of mixing arc code with html and javascript..?

Maybe you could elaborate on this a bit? If I understand correctly, then I'd respond that html.arc is not unlike Django's templating engine, and you're welcome to separate your code into different files if you prefer it that way. (You're just not forced to.)

> Thanks for your help, it's appreciated.

I hope this was of some help. Thanks for your post!

-----

1 point by evanrmurphy 5525 days ago | link | parent | on: Try Arc->Javascript

I forgot to mention macros. You won't be able to play with them on the demo page. In the current model, you define macros in Arc using js-mac and functions in Javascript using the compiler (with the (js `(def ...)) form). To clarify:

- Everything is written inside js.arc [1].

- js.arc has the axioms defined with defs and most of arc.arc's macros with js-mac;

- it also defops arc.js, which has most of arc.arc's functions defined in Javascript [2].

[1] http://evanrmurphy.com/js.arc, also defines some in-progress Javascript utilities (mostly DOM-manipulation) and the demo page itself toward the bottom.

[2] http://evanrmurphy.com/arc.js

-----

3 points by fallintothis 5524 days ago | link

I finally looked over your code a bit. Thoughts:

- Harumph. Looking back, I notice my ssexpand-all doesn't strictly work right. Instead of considering the actual cases, I just special-cased quotes and quoted unquotes. Technically, these are wrong.

  arc> (ssexpand-all '`(a:b:c d))
  (quasiquote ((compose a b c) d))
  arc> (ssexpand-all '(fn (x:y) x:y))
  (fn ((compose x y)) (compose x y))
  arc> (ssexpand-all '(assign x:y z))
  (assign (compose x y) z)
ssexpand-all should have a similar shape to macex-all, though it's more complicated and probably doesn't help much.

  (def imap (f xs)
    (when xs
      (if (acons xs)
          (cons (f (car xs)) (imap f (cdr xs)))
          (f xs))))

  (def on-qq (f expr)
    (list 'quasiquote
          ((afn (level x)
             (if (is level 0)
                  (f x)
                 (atom x)
                  x
                 (is (car x) 'quasiquote)
                  (list car.x (self (+ level 1) cadr.x))
                 (in (car x) 'unquote 'unquote-splicing)
                  (list car.x (self (- level 1) cadr.x))
                  (imap [self level _] x)))
           1 (cadr expr))))

  (def ssexpand-all (expr)
    (if (ssyntax expr)
        (let expanded (ssexpand expr)
          (if (is expanded expr)
              expr
              (ssexpand-all expanded)))
        (check expr
               atom
               (case (car expr)
                 quasiquote (on-qq ssexpand-all expr)
                 fn         `(fn ,(cadr expr) ,@(imap ssexpand-all (cddr expr)))
                 assign     `(assign ,(expr 1) ,(ssexpand-all (expr 2)))
                 quote      expr
                            (imap ssexpand-all expr)))))
Bah. It feels like I've written this code about a million times. Gross.

- I notice you wind up copy/pasting a lot of arc.arc definitions, particularly for macros. Couldn't it be more general? I.e., add a clause like

  (isa (car s) 'mac)   (js (macex1 s))
to js1. I figure I'd want any ol' macro to expand, just so I could reuse existing code. Plus, this gives you all those verbatim arc.arc definitions for free. But you'd still want js-macs, since there are certain macros that shouldn't "spill over". E.g., the non-verbatim arc.arc definitions need to be tweaked for Javascript, but shouldn't be globally overwritten. You could make js-macs take priority over regular macros by checking for them first in js1, and still reduce copy/paste work. Would anything break from this?

- Really, the point about macros expands into a broader one. My first inclination is to model the compiler on ac.scm; once that's working, you get arc.arc & libs.arc for free just by cranking the compiler over them. Keyword collisions could be mitigated as in ac.scm, which prepends underscores to variable names.

  arc> x
  Error: "reference to undefined identifier: _x"
But this "compiler-oriented" approach might not work well. There are the Javascript keywords that you want to use from Arc without them getting treated like variables (mainly the reserved words). It's interesting because this project isn't just an Arc-to-Javascript compiler, but also a DSL for Javascript in Arc. So there needs to be some sort of balance.

There are more "overlap-y" examples like while. It's implemented as a macro in arc.arc, but you'd probably want to compile it down to Javascript's while instead of tail-recursive function calls. Also, some features don't line up quite right, like first-class continuations. Though you could compile them into Javascript (that'd be cool!), a big use for them is simply

  (catch
    (while t
      (throw)))
which in more idiomatic (and probably more efficient) Javascript would be a while/break. So it isn't just calls to something specific like while, but also entire patterns of code that overlap. In the limit, you have Arc compile to complex Javascript, but I don't know how well Javascript handles as a target language for some of the crazier features like continuations.

I'm curious if you've tried this approach at all and, if so, how it panned out (I've never tried anything like it myself, so I wouldn't know).

Good work so far!

-----

1 point by evanrmurphy 5524 days ago | link

As I was going through copy-pasting and tweaking definitions from arc.arc, I thought several times that there should be a smart way to automate the process. (And it is true that a large portion of the defs and macs are verbatim.)

For a long time I didn't have js-mac and wasn't using js-def. Each macro got a handwritten js-<insert name> function and its own branch in js1's if; each function was handwritten in Javascript. [http://arclanguage.org/item?id=11918] I finally got smart enough to start using the partially bootstrapped compiler, and abstracting away the verbatim definitions should follow from that.

You could make js-macs take priority over regular macros by checking for them first in js1, and still reduce copy/paste work. Would anything break from this?

I do think that will be the way to do it, and it shouldn't break anything.

It's interesting because this project isn't just an Arc-to-Javascript compiler, but also a DSL for Javascript in Arc.

I knew this was true but hadn't yet found a way to articulate it. Thanks for the good choice of words.

There are more "overlap-y" examples like while. It's implemented as a macro in arc.arc, but you'd probably want to compile it down to Javascript's while instead of tail-recursive function calls.

Yes. Since resources are so precious in the browser and Javascript doesn't optimize tail calls, this will probably be an important refinement. As a bonus, it may make the compiled js more readable, since (while foo bar) would be generating something like while(foo){bar;}, rather than:

  (function(g3701){
    return g3701=(function(g3702){
      return (g3702?function(){bar;return g3701(foo);})['call'](this):nil);
    });
  })['call'](this,nil)(foo);
Also, some features don't line up quite right, like first-class continuations...

Your idea of compiling the common case to while/break sounds fine, but wouldn't try/catch be suitable as well? (I haven't made enough use of either first-class continuations or Javascript's try/catch to know yet.) There have been some cases I've dealt with already where the features didn't quite line up: rest/optional parms for functions, conses/lists vs. arrays and nil vs. false/null/undefined.

Thanks for the valuable feedback.

-----

1 point by rocketnia 5524 days ago | link

(fallintothis) It's interesting because this project isn't just an Arc-to-Javascript compiler, but also a DSL for Javascript in Arc.

(evanrmurphy) I knew this was true but hadn't yet found a way to articulate it. Thanks for the good choice of words.

I was also impressed by that choice of words. XD

-

wouldn't try/catch be suitable as well?

That's what I would use. That way a continuation could be called from within a (do ...) boundary.

-----

1 point by evanrmurphy 5525 days ago | link | parent | on: Try Arc->Javascript

What would be a good name for this project? I've considered:

  Arc->Javascript
  arc2js
  js.arc + arc.js
  JarcScript       ; if jazzdev doesn't mind :)
Also, I'm thinking of posting this on HN. Any suggestions on how to go about presenting it there?

-----

2 points by evanrmurphy 5524 days ago | link

garply may have taken the best name for his related project: arcscript. Wish I'd thought of that first. :)

Update: Another candidate is tojs, in the vein of aw's tojson and fromjson. I may be using these 3 programs together a lot for ajax.

-----

2 points by garply 5524 days ago | link

Please feel free to use arcscript. I only dumped that file into the repo because I had it lying around and thought someone might have use for it. You got around to completing a nice functional JS compiler implementation before I did and I'm glad you did. Feel free to upload on top of my file :)

-----

1 point by evanrmurphy 5523 days ago | link

Wow, thank you very much.

-----

1 point by fallintothis 5524 days ago | link

I think js.arc or arc2js work well, for what it's worth.

-----

1 point by evanrmurphy 5525 days ago | link | parent | on: Try Arc->Javascript

A simple front-end to this compiler I've been working on. The main (and only?) deviation from arc syntax has to do with object/list refs versus function calls. Because of the difficulty I had disambiguating the (x y) form, for the moment each case gets its own form as follows.

Function calls:

  (fncall x y) -> x(y);           ; verbose
  (x y)                           ; succinct
   x.y
  (fncall x 'y) -> x(y);          ; verbose quoted
  (x 'y)                          ; succinct quoted
   x!y
Object refs:

  (objref x y) -> x(y);             ; verbose
                                    ; no succinct form for no quotes
  (objref x 'y) -> x('y');          ; verbose quoted
  (x `y)                            ; succint quoted
   x.`y
List refs:

  (listref x y)  -> car(nthcdr(y,x));    ; verbose
  (listref x 'y) -> car(nthcdr('y',x));  ; verbose quoted
                                         ; no succinct forms for listref
Suggestions on how to improve this situation are welcome. I greatly dislike the lack of short forms for listref and non-quoted objref. The only perk is that since Javascript arrays are objects and numbers don't mind being quoted, you get array access for free:

  (objref (array 'a 'b) 0)  -> ['a','b'][0];
  ((array 'a 'b) `0)                                ; same as above

  (let a (array 'a 'b)      -> (function(a){
    a.`0)                        return a[0];
                               })['call'](this,['a','b']);

  (.`0 (array 'a 'b))       -> (function(_){        ; using get ssyntax
                                 return _[0];
                               })(['a','b']);

-----

1 point by evanrmurphy 5526 days ago | link | parent | on: Why make-br-fn?

Ah, you're right! I had thought it was ssexpansion because of

  arc> (ssexpand '[_])
  (fn (_) (_))
but it turns out to expand even without ssexpand:

  arc> '[_]
  (fn (_) (_))
Thanks for the clarification. ^_^

-----

3 points by evanrmurphy 5526 days ago | link | parent | on: Forum for basic questions?

I'm not sure, but maybe you need to do something like

  chmod +x racket
before it will work as an executable? You might also try the users@racket-lang.org mailing list, #racket on freenode.net, or one of the other resources at http://racket-lang.org/community.html to get input on this problem.

-----

More