Arc Forumnew | comments | leaders | submit | rocketnia's commentslogin
1 point by rocketnia 5606 days ago | link | parent | on: More Jarc and Rainbow bugs

For reference, the previous thread is at http://arclanguage.org/item?id=12269.

-----

1 point by rocketnia 5606 days ago | link | parent | on: Small bug in load

Hm, drat. This "fix" means that we have to be more careful with code that coerces symbols to strings and back, like fallintothis's 'sscontract.

I think I prefer original Arc's gensym hack for the time being, since I know that as long as I don't begin any symbols in my own code with "gs", they won't share their name with any gensyms.

With Anarki's approach, wouldn't it be harder to accomplish serializable gensyms? In official Arc, this kind of thing might be done on the reader side by replacing all read-in symbols that start with "gs" so that they don't conflict with gensyms currently in use. In Anarki, uniq!g1 and (uniq) can return gensyms with the same name, so there would have to be some special behavior on the writer side too.

To give a clearer example of what I'm talking about, a Racket-style serialization format would probably look something like this, where #u:X does what it can to create a new, uninterned X every time it's read in (and specifically works when X is a symbol):

  (1 #0=#u:this-is-a-gensym 2 #0#)

-----

1 point by aw 5606 days ago | link

I suspect that if we want to be able to produce unique symbols that can be serialized, we should use random symbols of sufficient length that the chance of collision is vanishing small (that is, similar to a UUID or GUID).

While a #u:X approach would work for a single output, if I produce multiple output files I have no reliable way to put the same uninterned symbol in different files.

-----

1 point by akkartik 5606 days ago | link

I'm not sure I follow, but the situation hasn't changed since August '09: http://github.com/nex3/arc/commit/47909c72a3e5ea7c4e2173fe61...

-----

3 points by rocketnia 5606 days ago | link

Right, but I wasn't familiar with it. This time, I thank you for pointing it out for me. ^_^

I meant to convey that I'd rather write Arc code so that sentinel values are generated using [] or whatnot, rather than introducing uninterned symbols to the language. Uninterned symbols make it much less of a pain for multiple threads to generate unique variable names, but they introduce complexity in other places. Besides the hypothetical examples I already mentioned, I'm happy I can enter (uniq) symbols at the REPL when I'm debugging.

I don't really expect this opinion to catch on, but I'm throwing it out there just in case.

-----

2 points by rocketnia 5606 days ago | link | parent | on: Small bug in load

How about replacing "w/uniq eof" with "let eof []"?

-----

2 points by aw 5606 days ago | link

Maybe not [], that relies on the language not ever getting serializable functions.

-----

1 point by rocketnia 5606 days ago | link

Agreed.

-----

1 point by akkartik 5606 days ago | link

I don't follow this exchange. Why would let eof [] work? And what do serializable functions have to do with it?

-----

5 points by aw 5606 days ago | link

We could use any unique value that compares equal to itself with "is" but isn't the same (by "is") as any possible value read from the input file.

An example of such a value is "(list 'a)". This creates a new pair (cons cell), which "is" itself, but "isnt" any other pair or value.

Another example is in Arc 3.1, fn returns a new #<procedure> which "is" itself but not any other function or other value.

"[]" expands into such a function, so it's a cute way of getting a unique value. However, for web applications it would be nice if closures could be serialized (written out to a file and read back in later), so that a server could be restarted without losing user's state.

If this were possible, then "[]", which expands into "(fn (_) nil)", could be written out to a file and read back in later.

Even if we could read in serialized functions, using [] as a sentinel value would still work if every evaluation of (fn ...) continues to produce a new, unique function, much like how "(list 'a)" or "(cons t t)" produces a new, unique pair every time.

Or maybe it would be useful for optimization or for some other reason for two identical functions "(fn (_) nil)" to evaluate to the same function object, much like how every time we read in a symbol 'x it evaluates to the same symbol. Or not... that might be a useless optimization (at least, I can't think of a use for it off the top of my head). But that was my thinking behind "maybe not []".

-----

3 points by waterhouse 5605 days ago | link

Incidentally, app.arc creates a unique "fail" global variable in this way. (Eeeeeaaaaaargh, never use asterisks in text. The word "fail" has an asterisk at the end. Is there a way to escape asterisks?)

  ; (= fail* (uniq))
  
  (def fail* ()) ; coudn't possibly come back from a form

-----

3 points by rocketnia 5605 days ago | link

You've even italicized the reply button. :-p I think the only way to "escape" an asterisk is to put whitespace after it: If you put a space at the end of "fail* ", presto. You could also subtly tweak your style in order to write fail* without quotation marks or other punctuation afterward. But yeah, escaping would be nice.

-----

2 points by rocketnia 5609 days ago | link | parent | on: How do you use arc in real world?

If you're just concerned about having the type 'vector, you can use Arc's (annotate type representation) function to give a value a wrapper with a custom type, and you can use the (rep wrapper) function to unwrap it.

If you're actually talking about vectors in the Scheme sense (arrays), there'll be a few more hoops to jump through, but that's not really what you mean, right?

-----

2 points by garply 5609 days ago | link

Thanks! I pushed a little bit of progress to lib/statistics.arc. I'd also like my vectors to function like lists.

  (= a (vec 1 2 3))
  type.a => 'vector
  (a 0) => 1
The list index referencing w/o first calling rep seems like it would require hacking ac.scm, right?

-----

2 points by rocketnia 5609 days ago | link

In Anarki, the hacking has already been done. ^_^

  (defcall vector (vec i)
    rep.vec.i)
The original 'defcall post is way back here: http://arclanguage.org/item?id=3743

Since then, it's been redefined in terms of [coerce _ 'fn] and an extensible implementation of 'coerce. http://arclanguage.org/item?id=9828 (Here you go, akkartik!)

Rainbow also supports 'defcall, but in the more direct way rather than through extensible coercion.

-----

1 point by garply 5609 days ago | link

This is great, thanks!

-----

1 point by alimoeeny 5609 days ago | link

Sorry guys, would somebody summarize this discussion on vectors and matrices? I mean after all is there any merit in defining a generic matrix type for everybody to use or people better define their own for their specific job. I am still trying to read the documentation, this is all new for me.

-----

2 points by rocketnia 5608 days ago | link

Well, what is there to summarize, hmm...

Looks like garply is putting together a library which deals with matrices and vectors and wants to be able to say (isa x 'vector). The 'annotate and 'rep functions make that happen. Anarki also defines 'defcall so that new types like these can be given special behavior when used as functions.

In one sense, garply's defining a matrix type for every Anarki user to use. In another sense, it's only a matrix type specific to the purposes of that library. But in any case, if this type doesn't look good to someone, they can just follow the same process to define their own type and forget this one exists at all. :-p

Realistically, I think efficiency is one of the things at the top of people's minds when they're trying to do computation with matrices, and with all due respect to garply, I doubt the project is going to get to the cutting edge of matrix efficiency anytime soon. >.> So I actually do expect someone to decide to define another matrix type later on. Nevertheless, garply's contributions could certainly help that person along. ^_^

-----

1 point by garply 5608 days ago | link

You're absolutely correct. I would go so far as to say that Arc itself is too slow to do any serious matrix computations (maybe if you made this just an interface to some Racket matrix libs you could work around it). I'm really not going to bother thinking about efficiency much at all. What I have done before in Arc is prototyped some algorithms on very small test sets. Once I got those working and figured out what I actually wanted, I rewrote everything in C++ or C, with a very close eye to efficiency.

I used to use this strategy all the time with an R / C combination, but I greatly prefer writing in Arc. Lush is kind of the best of both worlds, except the last time I pulled the bleeding edge version there appeared to be some crippling, hard-to-find bugs. Plus it's nice to just be able to build quick prototypes when you're already in Arc.

-----

1 point by rocketnia 5610 days ago | link | parent | on: Jarc 19 is out

I gave your SourceForge project a thumbs up, but it seemed to have registered it as a thumbs down. Maybe it doesn't like me. O_o I deleted the review, which succeeded in returning it to the way it was before.

-----

1 point by rocketnia 5610 days ago | link | parent | on: Jarc 19 is out

No Jarc 18? ^_^

I took a look at the code, and it looks like the vast majority of the change is the addition of 'readfns* and writefns*, which is pretty awesome in and of itself. You could definitely use that like reader macros for embedding templates for other language code in Jarc.

I couldn't help but notice that you fixed no more than one thing from (http://arclanguage.org/item?id=12269), the (string '|.|) issue, but no worries. :-p

-----

2 points by jazzdev 5578 days ago | link

I've been away from the forum (trying to get my startup launched). I'll look into the bug reports in http://arclanguage.org/item?id=12269 this week. Thanks for the bug reports, as always.

I've changed the default behavior for printing Java types that don't have a fn in writefns* -- Jarc now prints them as

  #package.class(hashCode)
It was getting annoying getting the missing writefns* messages. You can still define your own, but it doesn't pester you to do so anymore.

I'm working on a Rainbow compatibility module so that Jarc and Rainbow can share modules that call out to Java classes (like Swing and GAE datastore).

-----


I finally tried out Rainbow's profiler a bit. Awesome!

Of course, since I'm commenting here, it's because I found a bug. Rainbow's profiler chokes on function calls in optional arguments:

  arc> (profiler ((fn ((o x (do 1 2 3))) x)))  ; good
  3
  arc> (profiler ((fn ((o x [list])) x.0)))    ; good
  nil
  arc> (profiler ((fn ((o x (list))) x)))      ; not good
  Message    : Unhandled exception on thread#0: Unhandled exception on thread#0: null
  (...big stack trace...)
Also, the (system "open ...") call that opens the browser to the generated report page doesn't work on my current system (Windows XP without Cygwin). I was able to get around that by replacing it with a call to java.awt.Desktop.open(). ^_^

-----

1 point by conanite 5606 days ago | link

Cool, I'm glad you like it! Bugs noted ...

-----

1 point by rocketnia 5621 days ago | link | parent | on: Ask: Macros, names, and symbols

I'm assuming your code is a bit like this:

  (def tag (name contents)
    (+ "<" name ">" contents "</" name ">"))
  
  (mac def-tag (name)
    `(def ,name (contents)
       (tag ',name contents))
As you've figured out, (def-tag name) always expands to

  (def name (contents)
    (tag 'name contents))
even if you're not trying to define something named 'name. It's actually somewhat difficult to take a symbol you get at runtime and set a variable with that name. It can be done using (eval `(def-tag ,name)), but 'eval is usually overkill.

What you originally wanted was another macro that let you say (def-tags div span p). The first step to making that macro is figuring out what expression you want it to be an abbreviation for. In this case, it would probably be an expression like this:

  (do (def-tag div)
      (def-tag span)
      (def-tag p))
No 'each necessary. :) Once you've got an idea like that in mind, it's just a matter of picking the right tools for putting that expression together:

  (mac def-tags names
    `(do ,@(map [do `(def-tag ,_)] names)))
~~~

By the way, some of the particular names you're choosing are a bit dangerous. Arc programmers commonly use names like 'p, 'i, and 'b for local variables, specifically because they choose not to define macros with single-letter names. If you define those as macros before loading their code, then some of their function calls will be compiled as macro forms instead, and it won't be pretty.

On top of that, 'tag is already a macro that comes with Arc.

  arc> (tag foo (pr "contents"))
  <foo>contents</foo>"</foo>"
  arc> (macex1 '(tag foo (pr "contents")))
  (do (pr "<foo>") (pr "contents") (pr "</foo>"))
It comes with a bunch of other HTML-generation utilities in html.arc, like these ones...

  arc> (br 3)
  <br><br><br>
  nil
  arc> (underline:pr "contents")
  <u>contents</u>"</u>"
  arc> (hspace 4)
  <img src="s.gif" height=1 width=4>">"
  arc> (vhspace 4 9)
  <img src="s.gif" height=4 width=9>">"
  arc> (zerotable:pr "contents")
  <table border=0 cellpadding=0 cellspacing=0>contents</table>"</table>"
...which generally don't prevent people from making their own HTML libraries anyway. :-p These tools form the foundation of app.arc, blog.arc, news.arc, prompt.arc, and srv.arc, and they're not much more sophisticated than they need to be to serve those purposes.

-----

1 point by hasenj 5620 days ago | link

I know about html.arc, the point of the exercise is not to replace the builtin html macros, but to learn how to write macros (and how to think in Arc).

I have a couple of questions now:

1. Are macros defined in terms of 'eval'?

If there's such a thing as an anonymous function, why shouldn't there be an anonymous macro? Is eval the closest thing to anonymous macros?

2. Why would I want to my def-tags macro to be defined in terms of 'do'?

More generally, what the hell is the point of 'do' anyway? I would never have thought of doing anything inside a 'do' block, it seems rather redundant.

Note: I'm not trying to ridicule 'do', I'm just expressing my utter lack of understanding.

Btw, your guess of my code is pretty accurate, except I used string instead of +

  (def tag (name body)
    (string "<" name ">" body "</" name ">"))
~~~~

P.S. What's with this?

    arc> (underline:pr "contents")
      <u>contents</u>"</u>"
What's the extra "</u>" at the end? And why shouldn't I (or should I?) worry about it?

-----

3 points by fallintothis 5620 days ago | link

1. Not exactly, but it helps to think of it that way.

  (mac foo (x)
    (list 'bar x))

  (foo abcdef)
is conceptually like

  (eval ((fn (x) (list 'bar x)) 'abcdef)) ; = (eval (list 'bar 'abcdef))
Notice how the argument 'abcdef was quoted. Macros don't evaluate their arguments, but the code they generate might (e.g., if bar was a function, it'd try to evaluate abcdef as a variable).

They aren't actually implemented that way. eval operates at run-time and doesn't have access to lexical variables. Macros expand at compile-time, so it's as if you had the expansion there to begin with. E.g.,

  arc> (let y 10
         (eval 'y))
  Error: "reference to undefined identifier: _y"
but

  arc> (mac foo (arg) arg)
  #3(tagged mac #<procedure: foo>)
  arc> (let y 10
         (foo y))
  10
because

  (let y 10
    (foo y))
macroexpands into

  (let y 10
    y)
before it ever runs.

Anonymous macros are plausible, but they might force work to be done at run-time -- essentially, you're right that eval's the closest thing to it. But since macros happen at compile-time, you can't do something like

  ((if (mem 'x stuff) push pull) 'y stuff)
The compiler sees the macros push and pull, but they're not applied to anything, so it doesn't expand them. Then at run-time, you get an error as it tries to evaluate each form. You have a macro trying to act like a function (i.e., at run-time).

This topic comes up every so often: http://arclanguage.org/item?id=11517.

2. do is for when you want to execute a series of expressions, but do it in just one s-expression (i.e., the thing wrapped in do). You see it a lot in macros; e.g., defs in arc.arc:

  (mac defs args
    `(do ,@(map [cons 'def _] (tuples args 3))))
It converts

   (defs f (x) (+ x 1)
         g (y) (- y 1))
into

   (do (def f (x) (+ x 1))
       (def g (y) (- y 1)))
which is just one list. You couldn't return multiple values, so the macro couldn't expand into

  (def f (x) (+ x 1))
  (def g (y) (- y 1))
directly.

Another place you see do a lot is in if statements. Each clause is one expression, but if you want to do multiple things in one expression, you need the do. E.g.,

  (if test
      (do (pr #\t) (pr #\h) (pr #\e) (pr #\n))
      else)
will print "then" if test is true, otherwise it'll do else. This wouldn't work without the do:

  (if test       ; if "test"
       (pr #\t)  ; print #\t
      (pr #\h)   ; else if (pr #\h)
       (pr #\e)  ; then (pr #\e)
      (pr #\n)   ; else if (pr #\n)
       else)     ; then "else"
P.S. That's the return value of the statement.

  arc> (pr "a") ; prints "a" WITHOUT a newline, then returns the first thing it
                ; printed (the string "a")
  a"a"
  arc> (prn "a") ; print "a" WITH a newline, then returns the first thing it
                 ; printed (the string "a")
  a
  "a"
To learn more about macros, my debugger tool might be helpful: http://arclanguage.org/item?id=11806. Let me know if it is!

-----

1 point by hasenj 5618 days ago | link

on a second thought, the anonymous macro is nothing but the manual expansion of the macro :P

-----

1 point by shader 5617 days ago | link

That's not always true. If a programming system supported first class macros, then an anonymous macro could be passed in as an argument to a function, which could then apply it in a more sophisticated way at run time. I.e. apply it to a list of objects that don't even exist at compile time.

However, since arc does not support first-class macros, and neither do most compiled languages that I'm aware of, you're basically correct.

-----

2 points by rocketnia 5621 days ago | link | parent | on: Ask: html and javascript in arc

Here's the start of an approach loosely influenced by that:

  (def jquery-dedot (symbol)
    (when (isa symbol 'sym)
      (zap string symbol)
      (when (begins symbol ".")
        (cut symbol 1))))
  
  (def parse-chain (chain)
    (whenlet (first-method-sym . rest) chain
      (iflet first-method jquery-dedot.first-method-sym
        (let numargs (or (pos jquery-dedot rest) len.rest)
          (let (first-args rest) (split rest numargs)
            (cons (cons first-method first-args) parse-chain.rest)))
        (err:+ "A chain given to 'parse-chain didn't start with a "
               "dot-prefixed method name."))))
  
  (mac jquery (selector . chain)
    (let result `(js-call js-var!jQuery ,selector)
      (each message parse-chain.chain
        (zap [do `(js-send ,_ ,@message)] result))
      result))
  
  (def js-var (name)
    (annotate 'rendered-js
      string.name))
  
  (def js-call (callee . args)
    (annotate 'rendered-js
      (+ "(" tojs.callee "(" (intersperse "," (map tojs args)) "))")))
  
  (def js-send (object method . args)
    (annotate 'rendered-js
      (+ "(" tojs.object "[" tojs.method "]("
         (intersperse "," (map tojs args)) "))")))
  
  (def tojs (value)
    (caselet type-value type.value
      rendered-js  rep.value
      int          string.value
      string       (do (zap [subst "\\\\" "\\" _] value)
                       (zap [subst "\\'" "'" _] value)
                       (zap [subst "<'+'/" "</" _] value)
                       (zap [subst "]]'+'>" "]]>" _] value)
                       (zap [+ "('" _ "')"] value)
                       (tostring:w/instring stream value
                         (whilet char readc.stream
                           (let code int.char
                             (if (<= 32 code 127)
                               pr.char
                               (let hex (coerce code 'string 16)
                                 (pr "\\u")
                                 (repeat (- 4 len.hex) (pr #\0))
                                 pr.hex))))))
        (err:+ "A value of type \"" type-value "\" can't be "
               "translated by 'tojs (yet).")))
In action:

  arc> (jquery "#content" .toggle)
  #(tagged rendered-js "((jQuery(('#content')))[('toggle')]())")
  arc> (jquery ".cue" .html "done" .addClass "t")
  #(tagged rendered-js "(((jQuery(('.cue')))[('html')](('done')))[('addClass')](('t')))")
This code isn't necessarily shorter than the JavaScript code, but it's set up so that you can conveniently compute parameters from the Arc side via things like (jquery "#blah" .height (- full-height top-height)) while also being able to compute them naturally from the JavaScript side via things like (jquery "#nav" .height (jquery "#content" .height)).

One downside is all the parentheses this leaves. But if that turns out to be a problem, the thing I'd do is to rewrite the JavaScript-generating utilities to return meaningful AST objects rather than just tagged strings. That way, the AST can be pretty-printed in a separate step, once the context of each and every JavaScript expression is known.

An AST could also be good for displaying as debug output, minifying, verifying, compiling, translating to multiple programming languages, and doing any other kind of inspection. The format of the AST could also be set up to make it easier for different generated parts to avoid tripping over each other; for instance, there could be scopes for element IDs or a library dependency resolver.

I just finished converting my website's static site generator to Arc and Penknife (a language I've been making), and I'm using this kind of AST approach, if only 'cause HTML ASTs are so simple. :-p I haven't had the need to generate JavaScript or PHP yet--and I don't even use any PHP right now, lol--but I expect to get around to that someday, and at that point things'll get kinda interesting.

-----


Hmm...

  Jarc> (type:car:keys:obj (a) 1)
  java.lang.String
I suppose this inconsistency (if not bug) will have been smoothed out as a side effect if you ever end up fully supporting (def racket-equal? (a b) (.a:copy (obj) b t)). ^_-

As a general workaround guideline, I only use symbols as keys, 'cause I expect unexpected behavior like this if I don't. This case was an exception, but the workaround was still really easy, so don't worry about me too much. :-p

-----

More