Arc Forumnew | comments | leaders | submitlogin
1 point by hasenj 5140 days ago | link | parent

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 5140 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 5140 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 5140 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 5139 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 5140 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 5140 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 5139 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 5140 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 5139 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 5139 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 5140 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 5140 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 5140 days ago | link

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

-----