Arc Forumnew | comments | leaders | submit | EliAndrewC's commentslogin
7 points by EliAndrewC 4261 days ago | link | parent | on: The Seven Deadly Sins of Arc

As with most criticisms of Arc, these complaints are mostly treating Arc as if it was something other than a VERY early release of a language that's still under development. Both the syntax and semantics of Arc are expected to change as new features are considered/added/rejected.

However, it's easy to see why the author would think of Arc as something more than it is, since it was so hyped by so many people online and since it's been under development for so long. Plus, given the length of time that it's been out, most of us expected more progress by now.

Of course, this doesn't excuse the article's shortcomings, but I think there are a couple of valid points buried amidst the specious ones, so I'll respond to them all one at a time:

1. Criticizing Arc for a lack of features is pointless. It's an early release of the language, and there's no reason why more features can't be added later.

2. Paul Graham wrote an entire article on "Why Arc is not especially object-oriented" which links to several other articles by both himself and others discussing the pros and cons of OOP. The lack of namespaces is a valid criticism at the moment, especially since a lack of a namespace mechanism makes it more difficult for people to write and share Arc libraries.

3. Arc hasn't had enough releases for me to care about having access to PG's version control repository.

4. I'm not sold on Test-Driven-Design for software which is expected to radically change numerous times before becoming stable. I generally write tests after I've somewhat stabilized a codebase, and only then do I make any effort to keep my tests up to date. This is a nuanced issue and I respect the TDD arguments, but this certainly doesn't turn me off to such early versions of Arc.

5. This is another complaint about a missing feature. I agree that this feature should exist, but things like this don't sink the language.

6. Unusual version numbering is a deadly sin?

7. PG's stated outlook is that the more common an operation, the less intuitive its name can be. I don't find "no" less intuitive than "not", although I think he has a point about it being silly to abbreviate "print" to "pr" just to save 3 characters.

8. Line numbers is errors is my personal #1 desired feature for Arc, and hopefully this feature will be added soon.

Interestingly, his conclusion is entirely correct: "In short, if you were hoping for a usable and modern dialect of Lisp, then Arc is not the answer, and won't be without a lot of work." Arc will indeed take a lot of work to become modern and usable for most tasks, and I'm disappointed that PG hasn't put out a new release in over 9 months, but I hope that he'll eventually find time to take a more active role in Arc's development.


4 points by EliAndrewC 4309 days ago | link | parent | on: Where are we going?

I'd love for PG to spent more time per month on Arc, since to me personally it's probably the most interesting thing he does. I enjoy his essays and think YCombinator has produced some great startups which I sometimes use, but Arc is more interesting to me than either of those things.

With that being said, I knew all along that Arc is an experiment in trying to make a 100 year language, not something that was intended to help me deploy commercial applications as soon as possible. So I've played around with Arc a little and been happy to have the experience.

I'll play around with it more as it gets more advanced and gets more libraries and such. Until then, I'll remain a happy Python programmer and look forward to whatever PG and the rest of the Arc community slowly end up evolving towards.


1 point by EliAndrewC 4395 days ago | link | parent | on: Proposed new axiom: symeval

It seems like a module system could mitigate this problem. In other languages, functions resolve variables by first looking in their local scope and then by checking to see if the variable is global to the module/package in which it was defined.

I guess this is harder for macros, since they simply alter your code in-place and after the macro is executed, you have no way of knowing where the code came from. However, if we did have a module system, then you could refer to cplx->cplx-fun or whatever the module notation would be and thus the problem wouldn't occur.

Of course, this would require saying cplx-> or something like that in front of all of you calls to internal module functions/macros, which would be about as cumbersome as the proposed solution.

Perhaps we could have a type of macro which adds these module-specifying prefixes to your function calls when the macro is expanded. Of course, without knowing how the module system would even work, it's hard to gauge whether this would ultimately make things better or worse.


2 points by almkglor 4395 days ago | link

Depends on how the module system is constructed. If modules are first class objects and not a set of symbols like CL packages are, then the module name itself may be shadowed by a local, i.e. 'cplx itself could fall victim.

> Perhaps we could have a type of macro which adds these module-specifying prefixes to your function calls when the macro is expanded

If modules were based on symbols, like CL packages are, then:


2 points by cchooper 4395 days ago | link

CL packages solve this problem fine. I was hoping Arc wouldn't have to go down that route (because it always confuses newbies and adds a lot of complexity) but the more problems arise, the more I appreciate how good packages are.


2 points by EliAndrewC 4417 days ago | link | parent | on: Objects in Arc

I actually used "obj" as a reference when writing "new", which is why they were so similar. I decided not to use a gensym because I figured that "this" would effectively be a reserved word with a common meaning and so that I could use "this" in my methods. However, your point about clashes is well taken.

As for "defm", it looks pretty nice. I guess I wouldn't mind calling "(until p 30)" instead of "(p!until 30)" so long as I don't have to call "person-until" just to prevent naming clashes.

I really dislike having to call "((rep self) 'first)" instead of "self!first", but as you point out, I might be able to define some macros to make this less verbose.

I guess for now I'll start playing around with Anarki. Hopefully the more knowledgable Lispers out there will settle on an object system that's general enough to be useful and concise enough to by enjoyable.


3 points by absz 4417 days ago | link

Ah, I see -- new is an auto-binding obj. That makes sense if you're including methods (which perhaps you shouldn't). It's just that usually when people do that it is a mistake :)

Actually, that was a mistake on my part; you can in fact write

  (defm full ((t self person))
    (string self!first " " self!last)
  (defm until ((t self person) year)
    (- year self!age)
because the defcall allows you to put objects of type person in that position. And now the only redundancy left is having to write (t self person) all the time, which you may or may not want to abstract.

The other missing feature is the inability to say (= p!age 42); for that, you need to override sref:

  ; Insert error checking so that you can't
  ; write (= p!species 'banana).
  (defm sref ((t self person) value key)
    (= ((rep self) key) value))
Again, <plug>the tagged unions do all of this for you (except for defining your own methods, which you have to do with defm and vcase/tcase)</plug>. Of course, they don't have inheritance.


1 point by EliAndrewC 4417 days ago | link

That sounds excellent, and it makes me a lot more excited about using objects in Arc. I don't care that much about having to write (t self person) a lot, since it seems like an acceptably low amount of boilerplate.

However, out of curiosity, why do you have to write "t" instead of just saying "(self person)"? I read through the code for defm and argmap, but my Lisp reading skills aren't strong enough to decipher it.


2 points by absz 4417 days ago | link

Because parameter lists in Arc support destructuring. What that means is that anywhere you can write variable to bind a name to a value (such as in (let variable 10 (prn variable))), you can also write (v1 v2) to bind a list's first element to v1 and second element to v2. And (o v default) denotes optional parameters. Perhaps some examples would be clearer:

  (with (i 1 v 5 x 10)
    (let a            (list i v)   (prn "a is (1 5)."))
    (let (b c)        (list i v)   (prn "b is 1 and c is 5."))
    (let (d . e)      (list i v x) (prn "d is 1 and e is (5 10)."))
    (let (f (o g))    (list i)     (prn "f is 1 and g is nil."))
    (let (h (o j 42)) (list i)     (prn "h is 1 and j is 42."))
    (let (k (o m))    (list i v)   (prn "k is 1 and m is 5."))
    (let (n (o p 42)) (list i v)   (prn "n is 1 and p is 5.")))
defm adds a (t var type) syntax; if you left out the t, you would have ordinary destructuring bind.


2 points by EliAndrewC 4418 days ago | link | parent | on: Objects in Arc

Does this give you any kind of "this" object in your object methods? That's a large part of what I'm looking for, since that lets me say "(p!until 30)" in my example, or something like "(craa!act_bite)" in your example.


1 point by EliAndrewC 4418 days ago | link | parent | on: Objects in Arc

You are correct; it's also been awhile since I played with Javascript, but I just checked "A Reintroduction to Javascript" then it listed both my way and your way as examples, describing your way as being cleaner:


1 point by EliAndrewC 4418 days ago | link | parent | on: Objects in Arc

Wow, that's much better! I didn't think that would work, since I thought that Arc's lexical scope bound variables in inner functions to the current value of those variables. Apparently I was wrong; thanks for the tip.


11 points by EliAndrewC 4565 days ago | link | parent | on: New version

I like that "subseq" was renamed to "cut" and that it now takes negative indices.

However, I wish we could say (x 1 -1) instead of (cut x 1 -1). And since x.0.0 expands to (x 0 0) then that would let us say x.1.-1 which is what languages such as Python and Ruby already allow.

I hope that this is coming; I image that Arc's creators don't want to introduce too many changes all at once, so maybe they'll add this after they see how the x.y syntax works out.


8 points by sjs 4565 days ago | link

I would prefer it if (cut "abcde" 1 -1) returned "bcd" rather than "bcde". Other than that I like the new cut.


8 points by pg 4565 days ago | link

Hmm, yes, so would I. I should fix that. I just did what Emmett suggested, which must have been based on Ruby, but it doesn't actually seem like a good idea.


5 points by pg 4565 days ago | link



4 points by nex3 4565 days ago | link

Given this, I've changed it to 0-based reverse indexing in the Anarki.


3 points by jules 4564 days ago | link

In Ruby you use Ranges for this.

    (1..4).to_a => [1,2,3,4]
    (1...4).to_a => [1,2,3]
str[a..b] is a substring from a to b, inclusive.

str[a...b] is a substring from a to b, exclusive.

With negative indices:

    str[a..-b] == str[a..str.length-b]
    "abcde"[1..-1] == "abcde"[1..5-1] == "abcde"[1..4] == "bcde"

    str[a...-b] == str[a...str.length-b]
    "abcde"[1...-1] == "bcd"


1 point by carpal 4565 days ago | link

How else would you specify "the end of the string"? -1 seems natural for me. Anything else (0?) makes it kind of foggy.


5 points by nex3 4565 days ago | link

"End of string" is specified by not passing a fourth parameter:

  > (cut "abcde" 1)


2 points by maxwell 4563 days ago | link



2 points by nex3 4565 days ago | link

Agreed. Ruby does it the other way ("abcde"[1..-1] is "bcd"), but that's only because there's no other way to specify "until the end." But that's the default in Arc; the following are equivalent right now:

  (cut "abcde" 1 -1)
  (cut "abcde" 1)


3 points by lsb 4565 days ago | link

Nope, Ruby has -1 at the end:

    $ irb
    >> "abcde"[1..-1]
    => "bcde"


4 points by nex3 4565 days ago | link

Oh, right. That's what I meant.

So, to clarify: I think arc should not do it Ruby-style, but rather have

  arc> (cut "abcde" 1 -1)


2 points by mec 4565 days ago | link

How would you then decide 0 0?

  arc> (cut "abcde" 0 0)
  arc> (cut "abcde" 0 0)
-1 should deffinitly refer to the last elemental or I don't see a way to get it.


3 points by nex3 4565 days ago | link

(cut s 0 0) should return the empty string, just like (cut s 1 1) and (cut s 42 42). Which it does in either implementation.


2 points by vincenz 4565 days ago | link

Then you would need -0 to get the full string. You're not making sense.

How do you propose to get "bcde" from "abcde"?

Either you need -0, or your final 0 is ambiguous.


3 points by nex3 4565 days ago | link

No you don't - the full string is the default.

  > (cut "abcde" 1)


3 points by tokipin 4565 days ago | link

i was thinking that too, but how would you do it dynamically (eg with variables) ?

  (cut "abcde" begin end)
what would i need to put in end to get the full string? nil?


3 points by sjs 4565 days ago | link

Both nil and (len str) work. I see both sides of the argument as counting from -1 eliminates the 0 corner case.

I like indices that are intuitive with literal numbers. Counting from 0 at one end and from 1 at the other is jarring. When -1 points to the end of string rather than before the last char (cut str 0 (- (len str))) returns the first char instead of the empty string.

With -1 -> before last char:

  (def chop ((o str "abcdef"))
    (pr "Chop how many chars off the end of \"" str "\"? ")
    (= n (coerce (cut (readline) 1) 'int)) ; bug in readline prepends #\newline
    (prn "Chopped: \"" (if (is n 0) str (cut str 0 (- n))) "\"")) ; handle corner case
With -1 -> end of string:

  (def chop ((o str "abcdef"))
    (pr "Chop how many chars off the end of \"" str "\"? ")
    (= n (coerce (cut (readline) 1) 'int)) ; bug in readline prepends #\newline
    (prn "Chopped: \"" (cut str 0 (- -1 n)) "\"")) ; no corner case, but there's this -1 there
I probably made a stronger argument for -1 pointing to the end of string as it leads to shorter code.


2 points by nex3 4565 days ago | link

If you absolutely need to do that, you can just use (len str).


3 points by akkartik 4564 days ago | link

Yup. Which is how python does it.


2 points by jules 4564 days ago | link

    $ irb
    >> "abcde"[1...-1]
    => "bcd"


5 points by mdemare 4565 days ago | link

I've started a new thread with cut compared in Ruby and Arc:


2 points by nex3 4565 days ago | link

Agreed. On a related note, I also wish the first parameter could be negative as well.


3 points by chandler 4565 days ago | link

What you're proposing is an alternate non-parenthetical syntax (i.e. x.y.z => (x y z)). Careful! Down this road lies ruin.


4 points by EliAndrewC 4565 days ago | link

While I agree that this shouldn't be abused, I'm not actually proposing this syntax; Arc already lets you say cut.x.1.3 as an alternative to (cut x 1 3).

I suspect that the only time I'd ever want to chain together several different things with . is when I'm doing list slices. Which is why I'd like the x.1.3 syntax to work, since in my opinion cut.x.1.3 is a lot uglier, perhaps enough that I'd rather not do it.


3 points by EliAndrewC 4565 days ago | link | parent | on: New version

. and , are not very visually distinctive. It'd be like having (x 0) and (x O) both be commonly used.


1 point by EliAndrewC 4569 days ago | link | parent | on: Reference parameters

> However, A is a non-issue in Lisps and B is trivially solvable by returning a pair, so I don't see how pass-by-reference could be useful.

Is there an Arc equivalent to Common Lisp's multiple-value-bind macro? Because it's very awkward to have to say

    (let values (f x)
        (with (a (values 0)
               b (values 1))
            (whatever ....


3 points by rkts 4568 days ago | link


  (let (a b) (f x) ...)
although technically this is a destructuring-bind, not a multiple-value-bind.