Arc Forumnew | comments | leaders | submit | shader's commentslogin

I've created a system with multiple named pipes that I can reconnect to if necessary, mainly because I was trying to get an fcgi interface to work with arc.

-----

3 points by shader 5452 days ago | link | parent | on: Noobing with news.arc

Arc has a feature called "ssyntax", short for symbol syntax, that allows us to use certain characters in a symbol as shorthand for a commonly used longer form.

In this case, "karma.u" expands to (karma u), which is a macro that looks up the users karma. Functions applied to single arguments are extremely common in arc, so it is quite useful to have an abbreviation.

Other ssyntax include:

  a!b -> (a 'b)
  ~a -> (no a)
  a~b -> (compose a (no b))
  a:b -> (compose a b) ;(a:b c) is equivalent to (a (b c))

-----

1 point by markkat 5452 days ago | link

Ah... thanks. Great way to throw noobs like me off though. :) I was wondering about the colon too, I couldn't find an explanation anywhere.

-----

3 points by shader 5453 days ago | link | parent | on: Noobing with news.arc

Just one question: Is their karma supposed to be increased every time the log in and it's a new day, or every time they are logged in and it's a new day? I ask because I almost never have to log in to the arc forum; my session apparently lasts a long time, possibly lasting until I log in from a different computer or longer. As such, I never actually go through the official log in process, but I'm still online.

Does ensure-news-user get run on page load as part of the code that checks whether their logged in or not? From a cursory look at the code, it didn't appear to work that way.

How would you write it differently if you wanted to increase their karma for every day they logged on, as opposed to for day they logged in?

-----

2 points by akkartik 5453 days ago | link

I was wondering about that as well. Perhaps it makes sense to insert a hook (http://files.arcfn.com/doc/variables.html#defhook) into get-user (top of app.arc) rather than ensure-news-user.

You'd be adding some overhead to every single request. Not a big deal to begin with, but something to keep in mind.

-----


I would have a simple method for getting the code in plain text, but I wouldn't bother including dependency handling natively. I feel like all dependency checking could be done with extra code in the snippet and handled on the client side. That would be simpler and more open to changes in the future. Plus we don't have to spontaneously create a system that is both simple and comprehensive enough to satisfy everyone simultaneously ;)

Basically I would extend the news.arc code to include support for another item type - code - that can be submitted separately, named, searched for, and retrieved as plain text. Then all of the comments/discussion is included for free, and if the double indent was made slightly more intelligent it could extract code snippets from normal comments as well.

It doesn't seem like it would be too hard to implement this way (most of the code is already there) and if we can get pg to like it he might update the arc forum to support it ;)

-----

2 points by evanrmurphy 5452 days ago | link

> and if the double indent was made slightly more intelligent it could extract code snippets from normal comments as well.

I just noticed that UseTheSource, another news.arc forum, implements syntax highligting on the code blocks for some programming languages.

http://news.usethesource.com/formatdoc

-----

1 point by evanrmurphy 5454 days ago | link

I really like this pragmatic approach!

-----

2 points by shader 5454 days ago | link | parent | on: Access Lexical Variables

This looks promising.

Do you think it could be used to implement alphabetic br fns like I suggested at http://www.arclanguage.com/item?id=8997 or does the original problem still apply? (nested bindings are not detected properly at mac expansion time)

-----

2 points by fallintothis 5453 days ago | link

[Insert caveats about how crazy the idea is, yadda yadda, stuff rntz already pointed out, blah blah, who cares?]

There are too many edge-cases for the idea to work well once implemented (as I said in the giant rant above :P), but you can get the basic behavior down, which fixes the issues you were finding early on in the thread. I gather that's what you were asking. So, for the sake of fun:

  (mac lexbound (x)
    `(if ((locals) ,x)
         t
         (bound ,x)))

  ; ac won't care about shadowing a special-form, but we don't want to treat it
  ; like an arg to the br-fn below

  (def keyword (name)
    (or (nonop name)
        (in name 'nil 't 'if 'fn 'assign 'locals)))

  (mac make-br-fn (body)
    (w/uniq args
      `(fn ,args
         (with ,(mappend
                  (fn (v) `(,v (if (lexbound ',v) ,v
                                   (no ,args)     (err "Too few args to br-fn")
                                                  (pop ,args))))
                  (sort < (keep (fn (_) (and (isa _ 'sym) (~keyword _)))
                                (dedup (flat body)))))
           (when ,args
             (err "Too many args to br-fn"))
           ,body))))

  arc> ((make-br-fn (+ x y z)) 1 2 3)
  6
  arc> ((make-br-fn (+ x y z)) 1 2 3 4)
  Error: "Too many args to br-fn"
  arc> ((make-br-fn (+ x y z)) 1 2)
  Error: "Too few args to br-fn"
  arc> (let x 3 ((make-br-fn (+ x y z)) 1 2))
  6
  arc> (let + (fn (x y z) (prf "x = #x, y = #y, z = #z") (prn))
         ((make-br-fn (+ x y z)) 1 2 3))
  x = 1, y = 2, z = 3
  nil
  arc> ((make-br-fn (do (prs x z y) (prn))) 1 2 3) ; bound in alphabetic order
  1 3 2
  nil
You have to do the work within the expansion itself (at run-time), since that's where you need to check whether variables are lexically bound. I.e., if you do it in the macro, you'll just get the locals from macroexpansion time. This essentially works the same way the following do.

  arc> (let x (if (lexbound 'x) x 5)
         (+ x 5))
  10
since the call to locals winds up happening outside the outside of the fn that let generates:

  arc> (macsteps '(let x (if (lexbound 'x) x 5) (+ x 5)))
  Expression:

    (let x (if (lexbound 'x) x 5)
      (+ x 5))

  Macro Expansion:

      (let x (if (lexbound 'x) x 5)
        (+ x 5))

  ==> (with (x (if (lexbound 'x) x 5))
        (+ x 5))

  Expression:

    (with (x (if (lexbound 'x) x 5))
      (+ x 5))

  Macro Expansion:

      (with (x (if (lexbound 'x) x 5))
        (+ x 5))

  ==> ((fn (x) (+ x 5)) (if (lexbound 'x) x 5))

  Expression:

    ((fn (x) (+ x 5)) (if (lexbound 'x) x 5))

  Macro Expansion:

      (lexbound 'x)

  ==> (if ((locals) 'x) t (bound 'x))

  Expression:

    ((fn (x) (+ x 5)) (if (if ((locals) 'x) t (bound 'x)) x 5))

  nil
Whereas

  arc> (let x 10
         (let x (if (lexbound 'x) x 5)
           (+ x 5)))
  15
since the call to locals winds up happening inside the fn that the outermost let generates:

  arc> (macsteps '(let x 10 (let x (if (lexbound 'x) x 5) (+ x 5))))
  Expression:

    (let x 10
      (let x (if (lexbound 'x) x 5)
        (+ x 5)))

  Macro Expansion:

      (let x 10
        (let x (if (lexbound 'x) x 5)
          (+ x 5)))

  ==> (with (x 10)
        (let x (if (lexbound 'x) x 5)
          (+ x 5)))

  Expression:

    (with (x 10)
      (let x (if (lexbound 'x) x 5)
        (+ x 5)))

  Macro Expansion:

      (with (x 10)
        (let x (if (lexbound 'x) x 5)
          (+ x 5)))

  ==> ((fn (x)
         (let x (if (lexbound 'x) x 5)
           (+ x 5)))
       10)

  Expression:

    ((fn (x)
       (let x (if (lexbound 'x) x 5)
         (+ x 5)))
     10)

  Macro Expansion:

      (let x (if (lexbound 'x) x 5)
        (+ x 5))

  ==> (with (x (if (lexbound 'x) x 5))
        (+ x 5))

  Expression:

    ((fn (x)
       (with (x (if (lexbound 'x) x 5))
         (+ x 5)))
     10)

  Macro Expansion:

      (with (x (if (lexbound 'x) x 5))
        (+ x 5))

  ==> ((fn (x) (+ x 5)) (if (lexbound 'x) x 5))

  Expression:

    ((fn (x)
       ((fn (x) (+ x 5)) (if (lexbound 'x) x 5)))
     10)

  Macro Expansion:

      (lexbound 'x)

  ==> (if ((locals) 'x) t (bound 'x))

  Expression:

    ((fn (x)
       ((fn (x) (+ x 5)) (if (if ((locals) 'x) t (bound 'x)) x 5)))
     10)

  nil
That said, there are plenty of bugs.

- The most obvious is that you'd need a full-blown walker of the body argument to see where you really need to make new variables. E.g.,

  (make-br-fn (let x 5
                (+ x 10)))
macroexpands to

  (fn gs2296
    (with (+   (if (lexbound '+) +
                   (no gs2296)   (err "Too few args to br-fn")
                                 (pop gs2296))
           let (if (lexbound 'let) let
                 (no gs2296)       (err "Too few args to br-fn")
                                   (pop gs2296))
           x   (if (lexbound 'x) x
                   (no gs2296)   (err "Too few args to br-fn")
                                 (pop gs2296)))
      (when gs2296
        (err "Too many args to br-fn"))
      (let x 5
        (+ x 10))))
which winds up expecting an x argument, because it wasn't bound outside of the br-fn, even though it's just a local binding introduced in the body by the let.

  arc> ((make-br-fn (let x 5 (+ x 10))) 'ignored-x)
  15
- make-br-fn should probably start with (zap macex-all body), since macros may introduce arbitrary unbound variables. E.g.,

  arc> (macex1 '(prf "x = #x"))        ; introduces an unbound reference to x
  (let gs396 (list) (pr "x = " x))
  arc> ((make-br-fn (prf "x = #x")) 1) ; prf not expanded
  Error: "Too many args to br-fn"
Couple this with the above bug, and you have even more problems, since in the above expansion of prf, you can't tell that gs396 is a bound local in body without doing a code-walk.

- You'll still have problems from vanilla Arc's lexically-shadowed-macros bug in some cases, but they're the ones you'd expect from knowing about the bug to begin with. That is, the following works because do is not in a function position.

  arc> (let do 10 ((make-br-fn do)))
  10
But, instead of generating an error (applying 10 to 5), the following macroexpands do.

  arc> (let do 10 ((make-br-fn (do 5))))
  5
This thread's made local variables in Arc interesting in ways I hadn't thought of before. Macros introduce odd contours to lexical environments that don't exist in, say, Python (which still has a locals() function). Basically, any use of (locals) should come with a big, fat warning: "magic happens here".

-----

3 points by shader 5454 days ago | link | parent | on: Where can I find an ARC Programmer?

Right here. :)

However, being mostly language designers and/or people with day jobs and school to attend to, we're rather hesitant to write an entire application for you. Rather, usually we walk people through writing it themselves. If you can outline your problem more precisely, and then mention specifically you need help with, we're much more likely to help you.

So, what exactly is it that you want? Are you trying to get a computer application that you can use with your students? While we could help you, it's possible that having it written in arc isn't the best option.

Are you trying to learn to program with arc, and want to use this as a learning exercises? Great! We'd love to help you. Unfortunately, I'm not entirely sure what the application is supposed to do.

A little more information can go a long way towards helping us help you get what you want.

-----

2 points by shader 5454 days ago | link | parent | on: Share your useful functions/macros

Yeah, I've noticed that a lot of arc ideas have been posted as code in comments, instead of linked to on a source control site. This makes it so that the code is more likely to get lost, and harder to use in the first place.

To be honest, this is what I think people should be using the 'lib section of Anarki for; posting code that they think is useful. That way it's available for anyone that wants it. If you post code on the forum, post it somewhere else so that people can find it again.

Maybe people don't like using Anarki for that purpose, either because they're worried about dirtying the codebase, or because they just don't like using git. In that case, maybe we should make a server for arc hacks? Like an HN style arc-app that allows submission of code and comments, but with a search engine and an API for pulling code remotely. Thoughts? Maybe this should me moved to another thread.

-----

1 point by evanrmurphy 5454 days ago | link

> maybe we should make a server for arc hacks? Like an HN style arc-app that allows submission of code and comments, but with a search engine and an API for pulling code remotely. Thoughts? Maybe this should me moved to another thread.

Maybe aw's idea for a new code site could fill this role. Have you seen that recent thread? -> http://arclanguage.org/item?id=12920

-----

1 point by shader 5454 days ago | link

Possibly. At the time I wrote that I hadn't seen that yet, though I had seen previous ideas about an example system, etc.

Certainly, the ideas are related. I suppose any discussion should be on the other thread ;)

-----


The best part about arc is that it is so easily hacked. If you have what you think is a cool idea, why don't you try it and see? Show us the code and resulting use cases. If you aren't sure how, try a bit and then ask for help. We try to be pretty friendly around here if possible.

After all, the arc forum is really a community of language hackers. What better way to start than by making your own modifications?

That being said, I'm not sure whether to like this or not. The analysis required to determine which case to coerce to is getting rather complicated. I like code simplifications, but saving a single character (namely, an apostrophe) is a rather small difference vs the possible risk of making the code more confusing. However, if you show me... ;)

-----

1 point by hasenj 5461 days ago | link

I did try to give it a shot, but I'm not quite at the place where I can hack that much lisp.

-----

2 points by fallintothis 5460 days ago | link

If by "atom" you mean non-conses that aren't tables, strings, or functions:

  $ diff -u old-ac.scm new-ac.scm
  --- old-ac.scm  2010-12-01 10:18:36.961984734 -0800
  +++ new-ac.scm  2010-12-01 10:27:02.058606152 -0800
  @@ -647,7 +647,7 @@
   ;       ((or (number? fn) (symbol? fn)) fn)
   ; another possibility: constant in functional pos means it gets 
   ; passed to the first arg, i.e. ('kids item) means (item 'kids).
  -        (#t (err "Function call on inappropriate object" fn args))))
  +        (#t (ac-niltree (apply list fn (ar-nil-terminate args))))))
   
   (xdef apply (lambda (fn . args)
                  (ar-apply fn (ar-apply-args args))))

  arc> (1 2 3)
  (1 2 3)
  arc> (+ (1 2 3) (4 5 6))
  (1 2 3 4 5 6)
  arc> ('a 'b 'c)
  (a b c)
  arc> (map [1] ('a 'b 'c))
  ((1) (1) (1))
You could, of course, have another conditional (e.g., only do it for numbers instead of numbers, exceptions, quoted symbols, sockets, etc.). The catch-all seems dangerous. The special-casing hardly seems worthwhile. It saves you, what, a quote character for list literals (or otherwise a call to list, which isn't that much to type and makes the code clear that there's a literal list happening there)? Versus the other ideas kicking around in the comments of ac.scm, as seen in the diff.

-----

2 points by hasenj 5460 days ago | link

> a call to list, which isn't that much to type and makes the code clear that there's a literal list happening there

So far most macro-intensive lisp code isn't really clear until you know what the macro is doing, you could see (function arg1 arg2 arg3) inside an expression and you'd think it's a function call, but it could be passed to a macro and that macro would transform it into something else.

The advantage I'm hoping for is simplifying building lists (or trees) without resorting to macros.

Here's an explicit alist

  ((a b) (b c))
which otherwise would be a bit more cumbersome:

  (list (list a b) (list b c))
And this is not the same thing as

  '((a b) (b c))
tryarc:

  arc> (= a "w1" b "w2" c "w3")­
  "w3"
  arc> '((a b) (b c))
  ((a b) (b c))
  arc> (list (list­ a b) (list­ b c))
  (("w1" "w2") ("w2" "w3"))
I have to admit this is all somewhat theoretical at this point. For all I know, this pattern never occurs in lisp programs.

-----

3 points by fallintothis 5459 days ago | link

I have to admit this is all somewhat theoretical at this point. For all I know, this pattern never occurs in lisp programs.

Not enough that ambiguities wouldn't still need to be resolved with list anyway. Most of the time, you're not building up a literal tree of elements that you know will be atoms -- you're using variables. E.g.,

  (def enq (obj q)
    (atomic
      (++ (q 2))
      (if (no (car q))
          (= (cadr q) (= (car q) (list obj))) ; call to list here
          (= (cdr (cadr q)) (list obj)        ; call to list here
             (cadr q)       (cdr (cadr q))))
      (car q)))
You couldn't change (list obj) into just (obj), since (a) Arc doesn't like lexical bindings replacing macros, so (obj) would eval to an empty hash table; (b) even if that was fixed (it's easy to do, but vanilla Arc's still bugged), what if you're enqueuing a function, list, string, or hash table? (obj) would eval either to an error (not enough arguments) or would call your function, which is surely a bug. Thus, you need list.

  (def pair (xs (o f list))
    (if (no xs)
         nil
        (no (cdr xs))
         (list (list (car xs)))      ; calls to list here
        (cons (f (car xs) (cadr xs))
              (pair (cddr xs) f))))
could only be changed to

  (def pair (xs (o f list))
    (if (no xs)
         nil
        (no (cdr xs))
         (list ((car xs)))           ; since ((car xs)) would be a list
        (cons (f (car xs) (cadr xs))
              (pair (cddr xs) f))))
which isn't really clearer, and runs into the same problems if (car xs) happens to be a string, list, function, or hash table.

Even

  (def split (seq pos)
    (list (cut seq 0 pos) (cut seq pos))) ; call to list here
couldn't be changed, since seq should be a list or string.

See also the definitions in arc.arc of insert-sorted, reinsert-sorted, defsets of car/cdr/caar/cadr/cddr, setforms, a lot of macro expansions (like obj, though that's arguably replaceable under the proposed scheme), and commonest. That was my point about list making the presence of literal lists explicit.

Places where you could safely use this scheme aren't really big wins anyways. You get to change

  (def queue () (list nil nil 0))
to

  (def queue () (nil nil 0))
or

  (let record (list (seconds) ip user)
in app.arc to

  (let record ((seconds) ip user)
It's rare you'll have to type out a big enough tree literally (i.e., without variables that will cause the problems we've seen) to make it worthwhile, and it doesn't simplify much code that currently uses list anyway.

-----

1 point by akkartik 5460 days ago | link

What about

  `((,a ,b) (,c ,d))

?

-----

1 point by hasenj 5460 days ago | link

Too cumbersome and somewhat confusing.

Actually I think subconsciously I want to get rid of these symbols when ever possible.

More importantly, can you think of a disadvantage for implicit listing of expressions?

-----

2 points by waterhouse 5460 days ago | link

  ;(pmul-deg m) returns a function that multiplies polynomials
  ; and throws out all terms with degree > m
  ;xs is a list of polynomials
  
  ((pmul-deg 5) (car xs) (cadr xs))
I do not want this to be interpreted as an assoc-list.

-----

1 point by hasenj 5460 days ago | link

Right, and in this case I wouldn't want either.

What I'm proposing is, given a list:

  (x ....)
if x doesn't evaluate to a function, hash table, macro, (or whatever else is allowed as a first element), then as a last resort, we interpret the expression as a plain list

In your example, the first expression evaluates to a function, so the normal rules would apply as usual.

-----

5 points by waterhouse 5460 days ago | link

I see. I'll mention first that I wouldn't find this feature useful myself, and would likely be irritated that certain things didn't turn out to be errors. However, here's something I see as a problem that you can probably appreciate:

(1 2 3). Arc evaluates this expression. The car is a number. Therefore, this is interpreted as the literal list (1 2 3).

((1 2 3) 1). By the above, the car of this expression evaluates to the list (1 2 3). This, applied to the argument 1, should give us the second element (the 1th element with zero-origin indexing) of the list (1 2 3), which is 2.

(((1 2 3) 1) 6). Following the above method of evaluation, the car of this expression evaluates to 2, so we get (2 6), which you say should evaluate to the literal list (2 6). This is, of course, quite different from the list (((1 2 3) 1) 6), which is probably what someone who writes literal lists according to your system would expect. I don't think it's possible for (((1 2 3) 1) 6) to be interpreted as a literal list without throwing the Arc "data structure in functional position is interpreted as a lookup" model out the window.

For that matter, I think would already be pretty weird that, given that xs is (1 2 3), (xs u) will be either an element of xs (if u happens to be a number) or a literal list (if u isn't a number).

So, either you have a "literal lists" rule that works for nesting lists most of the time--just not when one of the lists happens to be length 2 and the cadr is a number--or you have to give up the "xs is a list --> (xs n) is the nth element of xs" rule. Perhaps you could restrict it to constructing lists of constant depth; that would be a consistent, easily understood rule that wouldn't infringe on list lookups, although it still would make (xs n) be a drastically different type of object depending on what n was, and I still wouldn't like it or find it useful.

Pedagogical digression:

You say this about using quasiquote in `((,a ,b) (,c ,d)):

> Too cumbersome and somewhat confusing. Actually I think subconsciously I want to get rid of these symbols when ever possible.

Then I think you are ignorant. My intent is not to insult you; I intend this as a statement of fact, and as implied advice (that you should learn more). Look at what quasiquote allows you to do:

  `((,a ,b) (,c ,d)) ;evaluate keys and values in assoc-list
  `((,a b) (,c d))   ;evaluate keys, not values, in assoc-list
  `((a ,b) (c ,d))   ;evaluate values, not keys, in assoc-list
  `(,(a b) ,(c d))   ;construct list of two function calls
By putting in a quasiquote and adding or not adding commas in various positions, you can specify any arbitrary pattern of evaluation for the list ((a b) (c d)). And I assure you, all of these are useful at times; above, I have merely listed ones that are common enough patterns for them to have names; attempting to define the language so that the compiler will always find the correct pattern and you'll never need to use quasiquote is a bad idea. And don't even try writing more than the most basic macros without quasiquote--it's like working without rlwrap, except that can be somewhat remedied by editing a file and defining (l) to load that file.

(Again, I don't intend to insult or flame you. I do intend to flame this idea, because I think it's bad.)

Philosophical digression:

Note, by the way, that the "data structures in functional position are interpreted as lookups" rule is pretty justifiable from a Lisp point of view. You could implement data structures as functions:

  (def my-cons (a b)
    (fn (x)
      (case x
        car a
        cdr b
        (if (isa x 'int)
            (if (is x 0)
                a
                (b (- x 1)))
            (err "Can't do this.")))))
  (def my-car (x)
    x!car)
  (def my-cdr (x)
    x!cdr)

  arc> (= x (my-cons 1 (my-cons 2 (my-cons 3 nil))))
  #<procedure: my-cons>
  arc> (x 0)
  1
  arc> (my-car x)
  1
  arc> (my-car (my-cdr x))
  2
  arc> (x 1)
  2
  arc> (x 2)
  3
The only issue is printing. Here's an example implementation:

http://pastebin.com/KK1bvv85

It isn't perfect, because that 'print function will apply any function to the symbol 'type, which may cause errors. You need some access to the implementation of primitive operators to do this right. But, of course, the language designer has that power, and could quite plausibly have implemented conses and tables as functions, and given 'pr what it needs. So, it makes sense from a very-basic-Lisp point of view that "(the-list-xs 2)" could be a function call that yields the second element of the-list-xs.

I'll add that numbers and atoms are... atomic. Pure and indivisible, the building blocks that you can make everything else in Lisp with. I'm fine with compound data structures being functions, because they can be implemented with functions as featureful as you want, but I think atoms should be simple, basic pieces.

-----

2 points by rocketnia 5459 days ago | link

I was going to say something almost just like this, so kudos. ^_^ However, I cut myself off 'cause I realized that in someone's mind, 6 could primarily be a function that constructs lists that begin with 6, and only incidentally has useful behavior with '+, '-, etc. :-p To be fair, that's pretty silly, and you have other points that are good regardless.

-----

1 point by hasenj 5459 days ago | link

I agree with the first part.

but:

> `((,a b) (,c d)) ;evaluate keys, not values, in assoc-list > ((a ,b) (c ,d)) ;evaluate values, not keys, in assoc-list

or, or ..

  ((a 'b) (c 'd))
  (('a b) ('c d))
Granted, this uses quote symbols too.

My point was finding ways to lessen the need for ` and ,

If [] wasn't already used for lambdas, I could've suggested using it as a raw-list literal. [1 2 3] wouldn't be that bad.

It's possible to use other symbols, like @[1 2 3] where '@[' is a single token, or @(1 2 3).

> Philosophical digression: (...) You could implement data structures as functions

Saw that in SICP :)

-----

2 points by fallintothis 5459 days ago | link

  ((a 'b) (c 'd))
  (('a b) ('c d))
I just noticed none of your alist examples work with the atoms-imply-lists thing -- unless you ditch list-indexing, like (xs 0). That is, even if a, b, c, and d are all atoms,

  ((a b) (c d))
would not be an explicit alist, since

  (a b) == (list a b)
and

  (c d) == (list c d)
Thus,

  ((a b) (c d)) == ((list a b) (list c d))
which throws an error since (list c d) isn't a proper index (i.e., isn't a number).

Even if you could write alists that way, you'd be restricted to only those with atom cars. Personally, I can't think of the last time I needed a literal alist. If I wanted to write them that much, couldn't use quote, and couldn't bare to use list, I'd probably just do

  (def assoc-list args (pair args))

  arc> (assoc-list 'a 1 'b 2)
  ((a 1) (b 2))
and do away with complicating evaluation rules in such fragile ways.

-----

1 point by rocketnia 5459 days ago | link

If [] wasn't already used for lambdas, I could've suggested using it as a raw-list literal. [1 2 3] wouldn't be that bad.

I don't think odd parentheses like [...] are substantially more convenient than operator applications like (list ...). In fact, when editing in a bare-bones text editor, it's a slight pain to have to figure out whether )))]))]))) is the right combination of brackets. :-p

That doesn't mean it's a bad idea altogether. I think Clojure probably has the best practical use of brackets. It uses [] for literal lists just like what you're talking about, but it also generally uses [] brackets wherever there's no operator-and-body format that needs highlighting and indenting. They're used for argument lists, for instance. I haven't heard of someone setting up an editor to take advantage of that consistency, but I'd be surprised if that wasn't the reason for it. ^_^

-----

2 points by hasenj 5459 days ago | link

> ))]))])))

One of the ideas lurking in my head was a symbol to close all open parenthesis

For example, assuming [] isn't used for anything:

  (def fun (args)
    (a (b (c (d)))))
would be written as:

  (def fun (args)
    (a (b (c (d))]  
where ] would tell the interpreter to close everything. Or maybe just close the nearest open parenthesis that's at the beginning of a line.

Granted, something like (a (b (c (d] looks a bit odd, but this looks less odd:

  (a (b (c (d (e (f (g (h)))]
And you'll be able to insert stuff in the middle without having to remember to balance parenthesis at the end:

  (a (b (c (d (x y) (z (e (f (g (h)))]

-----

1 point by rocketnia 5459 days ago | link

Didn't pg talk about this use of ] in one of the early posts on Arc?

I shy away from it only 'cause it reduces the number of editors which can make sense of the brackets.

-----

1 point by akkartik 5459 days ago | link

(Which is reason against PLT's use of [], but doesn't affect arc's chosen use.)

Incidentally [] has one major advantage over (): it doesn't require pressing the shift key every single time. In my vim and emacs I've swapped the two sets of keys in lisp mode.

-----

2 points by rocketnia 5459 days ago | link

Which is reason against PLT's use of [], but doesn't affect arc's chosen use.

Hmm? I don't provide any reasons against Racket's claim that "Using square brackets in a few key places makes Racket code even more readable." In fact, I think it does aid a bit in readability, but it doesn't help when my goal is to correct sloppy brackets. XD

What I am saying is that Arc's [+ 1 _] syntax is about as convenient as (f- + 1 _) or (f-:+ 1 _). Arc also shares the ))]))) issue, a little. It would be more noticeable if more operators accepted functions as their last argument rather than their first argument.

Incidentally [] has one major advantage over (): it doesn't require pressing the shift key every single time. In my vim and emacs I've swapped the two sets of keys in lisp mode.

You mentioned this a while ago, so I've been using only [] in my languages-in-progress. ^_^ It also helps that I begrudge () and {} for looking too similar to each other. :-p The one thing I'm worried about is that ] and [ might be less distinguishable from each other than ) and ( are.

-----

1 point by akkartik 5459 days ago | link

It would be more noticeable if more operators accepted functions as their last argument rather than their first argument.

Yeah, but they don't. Lisp idiom tends to be to put the values being operated upon last, and with good reason: you want to put last the arg most likely to be a temporary. Otherwise you risk separating function calls from their args. Compare:

  (some-function
     (some-verbose-computation
       ...
       ...)
     arg2 arg3)
with:

  (some-function arg2 arg3
    (some-verbose-computation
      ...))
Since there's this major structural constraint I think any dispatch in lisp should be on the type of the last arg. (http://arclanguage.org/item?id=12646)

-----

1 point by akkartik 5460 days ago | link

Hmm, it would involve reimplementing eval inside arc. So far the arc compiler simply converts arc expressions to scheme expressions. You can't evaluate anything then.

-----

2 points by fallintothis 5459 days ago | link

it would involve reimplementing eval inside arc

Guess I should've mentioned this in my first post. People shouldn't be getting hung up on it. The diff was in this function:

  ; call a function or perform an array ref, hash ref, &c

  ; Non-fn constants in functional position are valuable real estate, so
  ; should figure out the best way to exploit it.  What could (1 foo) or 
  ; ('a foo) mean?  Maybe it should mean currying.

  ; For now the way to make the default val of a hash table be other than
  ; nil is to supply the val when doing the lookup.  Later may also let
  ; defaults be supplied as an arg to table.  To implement this, need: an 
  ; eq table within scheme mapping tables to defaults, and to adapt the 
  ; code in arc.arc that reads and writes tables to read and write their 
  ; default vals with them.  To make compatible with existing written tables, 
  ; just use an atom or 3-elt list to keep the default.

   (define (ar-apply fn args)
     (cond ((procedure? fn) 
            (apply fn args))
           ((pair? fn) 
            (list-ref fn (car args)))
           ((string? fn) 
            (string-ref fn (car args)))
           ((hash-table? fn) 
            (ar-nill (hash-table-get fn 
                                     (car args) 
                                     (if (pair? (cdr args)) (cadr args) #f))))
   ; experiment: means e.g. [1] is a constant fn
   ;       ((or (number? fn) (symbol? fn)) fn)
   ; another possibility: constant in functional pos means it gets 
   ; passed to the first arg, i.e. ('kids item) means (item 'kids).
  -        (#t (err "Function call on inappropriate object" fn args))))
  +        (#t (ac-niltree (apply list fn (ar-nil-terminate args))))))
It works the same as any other list/table/string referencing in Arc. Things that look like function calls are compiled to (ar-apply f args), generally speaking (see ac-call), so this logic happens at runtime. Thus,

  arc> (let f [+ _ 1] (f 5)) ; evals as fn call
  6
  arc> (let xs '(a b c) (xs 0)) ; evals as cons ref
  a
  arc> (let xs "abc" (xs 0)) ; evals as string ref
  #\a
  arc> (let h (obj a 1 b 2) (h 'a)) ; evals as table ref
  1
In standard Arc:

  arc> (let x 'atom (x 5)) ; defaults to #t clause
  Error: "Function call on inappropriate object atom (5)"
With the patch:

  arc> (let x 'atom (x 5)) ; defaults to #t clause
  (atom 5)

-----

1 point by akkartik 5459 days ago | link

Ah, I did notice that. This thread feels like it's been going a long time.

-----

2 points by rocketnia 5460 days ago | link

In Anarki, you can try out something similar really quickly.

  (defcall int self-and-args
    copy.self-and-args)       ; The 'copy may be unnecessary....
This should cause (1 2 3) to evaluate to a list. You can do this for types other than 'int as desired.

I actually think your idea conflicts at a design level with 'defcall. With 'defcall, there's no real guarantee that custom types will use any given default behavior, so you can only rely on the default for built-in types, and at that point you might as well just 'defcall them all one by one rather than having a default.

-----

1 point by akkartik 5460 days ago | link

; the 'copy may be unnecessary....

Just use idfn.

-----

1 point by rocketnia 5460 days ago | link

How do you mean? Under certain implementations of defcall, you might be able to get away with (= call*!int idfn), but Anarki's current implementation is a shortcut for this kind of code instead:

  (defcoerce fn int (self)
    (fn args
      (apply list self args)))  ; might be able to use 'cons here...
My confusion about 'copy and 'cons is founded on this part of arc.arc:

  ; Can return to this def once Rtm gets ac to make all rest args
  ; nil-terminated lists.
  
  ; (def list args args)
  
  (def copylist (xs)
    (if (no xs) 
        nil 
        (cons (car xs) (copylist (cdr xs)))))
  
  (def list args (copylist args))
I don't hold onto argument lists very often, but when I do, I occasionally wonder whether I should be copying them first. I think this is the first time I've actually let that concern change my code, though. What do you think?

-----

2 points by aw 5460 days ago | link

In (fn args ...), args will be a MzScheme list terminated by Mzscheme's '() instead of Arc's nil. It's usually hard to tell the difference, since the Arc runtime treats '() like nil when it can. There are a few odd corner cases where you'll notice it, for example if you use args as a key in a table, it will be a different key value than if you construct the same list using list.

-----

1 point by rocketnia 5459 days ago | link

Ah, right. Part of me's trying to think of a way to use an extensible 'iso (like akkartik's or mine) so it could be used as a table key comparator, but without the burden of maintaining a corresponding extensible hash function. (That is, the hash function would be automatic somehow.) How feasible do you think that would be? Maybe we should have the extensible hash function, but have convenience macros that extend both functions at once?

My main motivation is to remove all traces of the non-extensible Racket 'equal?.

-----

2 points by elibarzilay 5459 days ago | link

Not that it matters much in this context, but racket's equality is extensible: http://docs.racket-lang.org/reference/booleans.html#(def._((...)

-----

2 points by akkartik 5459 days ago | link

That is awesome, thanks for the tip.

Now I want to rant about Racket documentation.

You tell me equality is extensible, and now I know that, and I go look at that page and scan it from top to bottom. I do the usual things I do to read stuff online, and I've been pretty successful with my reading, my reading comprehension scores were excellent in standardized tests, I have a phd that was about nothing if not reading opaque publications, and now I'm faced with this page and I still have no idea how to tell that equality is extensible, or how to go about extending it. If you hadn't told me I'd look at that page about booleans and have no reason to expect it to bear the slightest relationship to equality for complex user-defined data structures[1]. Search for 'extensible'. Nope. Oh, 'inspectable structures.' What the sam hill are those? It has to do with prop:equal+hash, which doesn't even look like a single token to my eyes. Is it a function? How do I use it? All this goes through my mind in a flash, and all I feel is stupid.

I keep waiting for the racket reference to gradually become clear, but it's been a year now of poring over it and I don't think it'll ever happen. It's a blind spot, platform nine and three quarters, just around the corner but at ninety degrees from all three dimensions that I can see. I would never ever have noticed that equality is extensible until you told me that was so.

[1] I scan back up and it's right there in the title: "Booleans and equality." See what I mean about feeling stupid? Why should booleans and equality be grouped together? Why should equality be in the section on datatypes and not say structures?

-----

2 points by elibarzilay 5459 days ago | link

0. Note that the actual link is broken -- it's missing a ")" in the end (it's there, but it wasn't included in the url for some reason (I don't know about the local markdown language...))

1. Yes, some of these issues are known, and we're in a constant race with improving the documentation in various ways. As always, emails to the mailing list or bug reports -- or better: suggestions and possibly patches -- all of these are always welcome.

2. In this particular case, I didn't need to guess -- I knew that it was added, so I just needed to find that reference.

3. But if I were trying to find it, the first place I'd look would be the documentation for `equal?' -- and it is indeed there, at the end of the second paragraph.

4. As for how you use this property, the text that I referred to has a link to "Structure Type Properties", which describes all of that.

5. Re the organization -- booleans and equality are grouped because they're related... It also appears as the first subsection in the datatypes chapter, which makes sense there. If you have an idea how to organize it better, then see #1.

6. Yes, it would be nice to have some section that lists all of the properties that could be used for structs. The main problem with doing this is that it's an open system, so anyone can add more properties, but it won't make sense for the core to list properties from modules outside of it. This was discussed recently, and I don't think that anyone had an idea what to do about it.

-----

1 point by akkartik 5459 days ago | link

"if I were trying to find it, the first place I'd look would be the documentation for `equal?' -- and it is indeed there, at the end of the second paragraph."

Part of the problem is that I never tried finding it, because it didn't occur to me that racket would have extensible equal?

A few months ago I was flattened - absolutely flattened - to find out that PLT has optional args and keyword args. (http://arclanguage.org/item?id=12591)

I have no idea why this is. Perhaps the problem is that people expect scheme to be a small language.

-----

3 points by elibarzilay 5459 days ago | link

Well, the optionals and keyword arguments have been in for a long time now... In fact, the current thing is a second iteration after a first facility that was more CL-like...

In any case, Racket is certainly not a small language...

-----

1 point by akkartik 5459 days ago | link

Yes, as I was ranting I was feeling bad for not having contributed to improve things. I wasn't being rhetorical about feeling stupid and guilty. Lack of understanding is a barrier but no excuse. For me to say "I have a phd, but this I can't read" is akin to saying "I've learned spanish, but chinese is hard." Well, d'uh. Deal.

Perhaps PLT needs a user guide in addition to a reference, a level of redundancy with a kinda orthogonal organization (focusing on complex tasks involving multiple advanced concepts) that'll help people triangulate on understanding, or just use what works for them.

-----

3 points by elibarzilay 5459 days ago | link

Heh, excellent idea: http://docs.racket-lang.org/guide/

(See also other such documents at http://docs.racket-lang.org/getting-started/)

-----

1 point by akkartik 5458 days ago | link

I've seen that before. Why have I not paid more attention?

Here's the extremely clear guide on extensible equality: http://docs.racket-lang.org/guide/define-struct.html#%28part...

I'm going to withdraw my rant. It's something specific about my stupidity that's causing me these problems. Still investigating.

Ah, I think I see what's been happening. Since I started out with arc I've restricted myself to the mzscheme 'ghetto' and not paid as much attention to the racket language itself. My attitude was "who knows what works in mzscheme and what doesn't." This has been the root cause of my troubles, I think.

I'm going to focus on core racket now.

-----

2 points by evanrmurphy 5459 days ago | link

Thanks for the link to Guide: Racket. I've also had trouble getting into Racket's documentation, but this looks like a much more accessible starting point than Reference: Racket.

-----

1 point by rocketnia 5458 days ago | link

(Fixed link: (http://docs.racket-lang.org/reference/booleans.html#(def._((...). I fixed it by putting another pair of parentheses around it to fool the regex. :-p )

Oh, awesome. At one point I think I knew that, too, but at some point I saw 'make-custom-hash (http://docs.racket-lang.org/reference/dicts.html#(def._((lib...) and forgot about it.

Given that prop:equal+hash exists, are there any commonly used convenience macros for implementing it for a structure? It's niftiest if they support cyclic structures too, since Racket's equality API makes that possible, but I'm not picky. ^_^ I'll probably just write something myself either way, but I'm on the lookout for inspiration.

-----

1 point by akkartik 5460 days ago | link

Oh, I didn't think that much about it. I just tried this in anarki:

  (defcall int self-and-args
    idfn.self-and-args)
and it seems to work ^_^. I can't come up with a counterexample that requires copying. Can you? Otherwise let's just not copy until something breaks. Then we'll learn something.

-----

1 point by akkartik 5460 days ago | link

You know I think I totally misunderstood your original code comment. When I read it now my response to use idfn seems kinda facetious (I was seriously suggesting it, don't know what I was thinking.)

-----

1 point by rocketnia 5459 days ago | link

Well, I don't hold it against you. :-p It gave me an excuse to elaborate about my confusion too. >.>

I think I agree with you that we shouldn't worry about holding onto the argument list as long as it isn't a tangible problem. I'm interested to hear if someone knows of a problem though. ^_^ (Edit: Oh, aw posted an example.)

-----

1 point by shader 5462 days ago | link | parent | on: Question: How do I use Arc?

I prefer Mint mainly because of the decent looking initial theme, and the superior driver support. I've almost never had any issues getting Mint to install properly with everything working on the first try. Also, the install and configuration process is extremely streamlined and easy to follow. Not that Ubuntu is bad, per se, but Mint seems marginally better.

I'd definitely recommend Mint first for someone not yet well acquainted with linux. The ease of install, simple menu, and software installation tool make it a lot nicer on new users.

I run a virtual boxed Mint in seamless mode on Windows 7 continuously, to use as a shell for ssh and programming in certain languages (like arc or c), and I use a heavily modified Mint on my netbook as well.

-----

2 points by shader 5470 days ago | link | parent | on: QuickCheck for Arc

Very nice, clean and easy to use unit test system for arc. It seems like a very flexible system, and the generators idea makes it seem pretty extensible. On the whole its a fantastic piece of code that demonstrates clearly the readability and conciseness of arc (presuming it works of course :P ). I think it would make a great addition to Anarki.

One thing that might be nice to show in your tutorial is how to use it more like a traditional unit test syste. i.e. specify ranges of values to test. I don't mean write lots of test cases, but rather an example generator that produces all of the important edge cases, and then as many as possible inside the desired range. I would look at this more myself right now, but bit bucket seems to be down.

I do have one comment though (concerning the wiki): rev is not idempotent. Technically, an idempotent function f is one such that

  (f (f x)) = (f x)
Absolute value is a good example of such a function.

However,

  (rev (rev x)) = x != (rev x)
The correct term seems to be "involutary", according to http://en.wikipedia.org/wiki/Involution_%28mathematics%29

-----

3 points by fallintothis 5453 days ago | link

Okay! I've finally gotten around to writing this. Sorry it took so long.

One thing that might be nice to show in your tutorial is how to use it more like a traditional unit test syste[m].

Well, they serve different purposes. QuickCheck properties are universally quantified: every x should satisfy (p x). There are obviously practical problems with testing these, so we use a flood of random values for x. Unit tests are existentially quantified: for this particular x, (p x) should be satisfied.

In practice, people unwittingly reinvent one in terms of the other. In a unit-testing library, they'd change the existential property into a "universal" one, in the sense that we can test random values (which is as universal as we usually get).

  (unit-test test-foo
    "foo holds for strings"
    (let x (rand-string (rand max-int*)) ; whatever max-int* is
      (foo x)))
This is an anti-pattern that is solved more cleanly with QuickCheck by abstracting away the random generation.

  (prop prop-foo (x 'string)
    "foo holds for strings"
    (foo x))
Conversely, we can change a universal statement into an existential one easily. Just don't use any parameters to prop.

  (prop prop-foo ()
    "foo doesn't break on an edge case"
    (let x edge-case
      (foo x)))
This, too, is an anti-pattern. E.g., a unit-testing library would probably have better facilities for shared data set-up/tear-down, grouping together tests into suites, and asserting several things per test. (The last one's not necessarily true if we try to keep "functional-programming-y" about it like QuickCheck does.) As it stands, this is a square peg in a round hole for quick-check.arc. It'll run (prop-foo) 100 times, testing a property that's verifiable after only 1 run.

In a way, QuickCheck properties seem more powerful. You test a range of random data instead of the same ol' '(8 6 7 5 3 0 9), "foobarbaz" test inputs. QuickCheck lets you discover edge cases. In the presence of such a tool, unit-tests become less of a default ("Agile Methodology dictates we write unit tests first!!") and more of a backup plan in the fight against regressions: discover edge cases with QuickCheck, then make sure those edge cases never break your code again with unit tests.

Even so, you'll still want to write unit tests for other reasons. If you know a priori that a certain input should produce a certain output, it's easy to put that in a unit test. Often, to test it as a universal property basically relies on the very program you're writing. Silly example, for illustration: converting Roman numerals to integers. You could write

  (generate roman-int (int-between 1 4000))

  (prop test-roman (x 'roman-int)
    (is x (roman->int (int->roman x))))
which is a useful property, but relies on both roman->int and int->roman having been written correctly. As long as int->roman and roman->int are inverses, they'll pass the property. They could even be something stupid like

  (def roman->int (x) (+ x 5))

  (def int->roman (x) (- x 5))
So you need to test just one of them. How do you do that?

  (is (int->roman x) ???)
x is random, so you won't know what the other side of this equality should be. In this case, you'd want unit tests against known values -- 1 and "I", 4 and "IV", etc.

This problem with inverses actually came up when I was testing sscontract (http://arclanguage.org/item?id=11179), hence test/canonical.arc: http://bitbucket.org/fallintothis/contract/src/d1b4ff38afaf/....

Now! You probably knew that stuff already. But it gives us a framework for my understanding, so we can discuss the rest of your post.

i.e. specify ranges of values to test. I don't mean write lots of test cases, but rather an example generator that produces all of the important edge cases, and then as many as possible inside the desired range.

I'm not exactly sure what you mean. Per the above, it doesn't sound like a "traditional unit test system". Specifying ranges of values sounds like what QuickCheck is supposed to do. But there's no real way of automatically knowing all of the important edge cases, so I assume you mean that edge-cases are provided manually. That seems to contradict your "don't write lots of test cases" line, though. Finally, "as many as possible inside the desired range" makes me think you're talking about disjoint input sets to a single property: first test all of the edge cases, then start randomizing.

Using quick-check.arc to do the random testing it's meant to do, that's

  (sized edge-case n
    (rand-choice      ; or frequency, if you want to bias the choices
      edge-case-1
      edge-case-2
      edge-case-3
      (arbitrary default-range n)))

  (prop foo (x 'edge-case)
    (bar x))
But that won't enumerate all of the edge cases. It will with fewer than 100 of them, on average, but it's all still random. You could un-randomize the generation a little bit with something like

  (let index 0
    (sized edge-case n
      (if (< index (len list-of-edge-cases))
          (do1 (list-of-edge-cases index)
               (++ index))
          (arbitrary default-case n))))
Then each call goes through list-of-edge-cases in sequence. Once it gets to the end, it starts pumping out arbitrary values.

  arc> (= list-of-edge-cases '(foo bar baz))
  (foo bar baz)
  arc> (= default-case 'int)
  int
  arc> (arbitrary 'edge-case 10)
  foo
  arc> (arbitrary 'edge-case 10)
  bar
  arc> (arbitrary 'edge-case 10)
  baz
  arc> (arbitrary 'edge-case 10)
  -10
  arc> (arbitrary 'edge-case 10)
  -5
If you have more edge-cases than the number of times quick-check runs (presumably 100, though sometimes more with whenever clauses and such), you still won't go through all of them. So you could manually go through each edge case separately from the quick-check call, then run quick-check, and...

Square peg, meet round hole.

However, this thread's gotten me interested in writing a proper unit-testing library for Arc. The main considerations are API design and the output's readability. After figuring those out, the programming part's easy. And there are lots of example libraries to base it off of. We'll see if I get around to it any time soon. (Maybe this weekend? No promises, though; I've got a bunch of work to do.)

What becomes important and interesting, then, is to automate the synergy between QuickCheck and unit tests. Instead of "run QuickCheck, get a failure, copy/paste into a unit test, fix code, run QuickCheck again, pass, run unit tests, pass", there should be a way to spill over QuickCheck failures into unit tests transparently, so we don't even need to think about it. Again, easy enough to do, once I settle on the way it should be done. Is that closer to what you meant?

-----

2 points by akkartik 5453 days ago | link

(Request: please post an email on your profile.)

"this thread's gotten me interested in writing a proper unit-testing library for Arc.. automate the synergy between QuickCheck and unit tests. Instead of "run QuickCheck, get a failure, copy/paste into a unit test, fix code, run QuickCheck again, pass, run unit tests, pass", there should be a way to spill over QuickCheck failures into unit tests.."

I'd love to help. I've been testing a lot with arc. Readwarp has more tests than code.

One recent idea to improve testing is to run tests in dependency tree order: http://arclanguage.org/item?id=12721

-----

1 point by fallintothis 5470 days ago | link

I do have one comment though (concerning the wiki): rev is not idempotent.

Egads! How embarrassing. That'll teach me to double-check my abstract algebra. :) Thank you, and fixed.

but bit bucket seems to be down

Yeah, there were notices about some maintenance thing or other. Side note: the /src/ vs. /src thing seems fixed now.

One thing that might be nice to show in your tutorial is how to use it more like a traditional unit test syste[m].

I have plenty to say about that, but I can't type it all right now, so I'll have to get back to you in a little bit. Sorry!

-----

1 point by rntz 5469 days ago | link

rev isn't idempotent, however, rev:rev (rev composed with itself) is, which is I suspect what fallintothis was thinking. "Involutary" (which term I've never run across prior to this) appears to be a term for just this property: that the square is idempotent.

-----

2 points by shader 5469 days ago | link

Involution is more strict than the square being idempotent; it also must be the identity function. I.e. abs^2 is idempotent but abs itself is not involutary, since abs^2(-1) = 1 != -1

-----

More