Arc Forumnew | comments | leaders | submit | akkartik's commentslogin
3 points by akkartik 4885 days ago | link | parent | on: Why Arc hasn't taken off

Thanks! I woulda missed that if you hadn't posted this. It got me to finally post a long-gestating comment on arc's continuation-based webapp framework: https://news.ycombinator.com/item?id=5032739

-----

1 point by kinleyd 4885 days ago | link

That was good post by you on HN.

-----

2 points by akkartik 4885 days ago | link

I take it all back. See PG's response there: https://news.ycombinator.com/item?id=5035857

-----

2 points by rocketnia 4884 days ago | link

Racket already represents one approach to "trying to scale continuation-based servers," thanks to its stateless servlets.

http://docs.racket-lang.org/web-server/stateless.html

"This process allows [many] continuations captured by your servlet to be serialized. This means they may be stored on the client’s browser or the server’s disk."

-----

2 points by akkartik 4884 days ago | link

Yes, I saw that between my comment and pg's response.

-----

1 point by rocketnia 4884 days ago | link

Oh, I was particularly responding to "It also turns out that there's at least one company trying to scale continuation-based servers," which you said afterward. Guess you chose the example you chose, no worries. ^_^

-----

1 point by akkartik 4884 days ago | link

Ah, good point! I didn't think of it then (I think I even had the racket link in the textarea in a different context before I ended up dropping it to the floor.)

-----

1 point by kinleyd 4884 days ago | link

Well, at least you got a response from pg regarding Arc. Now, about that outdated Arc home and install page ... :)

EDIT

More seriously:

pg "In retrospect YC would have taken over my life no matter what. It has pushed out essays too, mostly.

I don't consider Arc to have died, incidentally. You used it to say that, and I'm using it to reply. If I ever retired from YC I'd probably start working on it more actively again." [1]

The thread was quite revealing. Or perhaps it just said what we all probably already knew.

[1] http://news.ycombinator.com/item?id=5035846

-----

1 point by akkartik 4882 days ago | link

Oh, and PG has said before that closures are a rapid-prototyping technique: https://news.ycombinator.com/item?id=3099372 (via http://arclanguage.org/item?id=17279; thanks rocketnia!)

-----


My favorite comment on the HN thread[1] so far points out a new design tradeoff rather than retreading tired aesthetic preferences:

"..indentation is not affected by the lengths of identifiers. It’s silly to format code in such a way that you need to re-indent just because you renamed a function." http://news.ycombinator.com/item?id=4993847

[1] http://news.ycombinator.com/item?id=4992603

-----

2 points by rocketnia 4893 days ago | link

"..indentation is not affected by the lengths of identifiers. It’s silly to format code in such a way that you need to re-indent just because you renamed a function."

That silliness is hard to avoid. If we don't use word wrap, we have to re-indent code when an identifier changes just so things stay on the screen. To avoid that re-indentation, we should probably redesign our text editors, diff tools, and languages so word wrap isn't a pain to work with. I think I'd rather do a little re-indentation for now.

---

It's not quite the same topic, but for a long time,[1] I've considered indentation an enemy to brevity. All that wasted whitespace! :) Arc's a:b ssyntax is great at eliminating levels of indentation.

[1] The only evidence I can find is http://www.arclanguage.org/item?id=14077, where I say a new ssyntax proposal "wouldn't [...] save much indentation."

-----

1 point by akkartik 4893 days ago | link

Just do what I do and don't have hard limits on line width :)

http://github.com/akkartik/wart/blob/44423b4107/011types.cc#...

I do wrap lines on occasion, but I wouldn't do it just because a line's a little too long.

-----

1 point by rocketnia 4893 days ago | link

I don't quite know why, but I like to be pretty strict. I keep every line at 70 spaces long or less, with tab stops every 4 spaces, except for the following scenarios:

- Commented-out lines of code.

- A line of code that just can't be broken up any further. But first I try to move this line to a less indented part of the code, and then I remove other indentation on the line until it fits the best it can.

- Code that someone else has already made too long by my judgment. I try not to change someone else's style if my cosmetic change would obscure the meaningful changes I'm making, or if it would introduce inconsistency in the codebase. In this case the codebase includes any part of the revision history, branches, forks, and related projects that might be reintegrated.

In college I used 80 spaces, but that turned out to be too long to fit on my Wordpress layout without scrolling.

---

I thought it was also too long for Arc Forum, but it seems that's not true. Looks like Arc Forum supports 83 characters, at least on my user agent.

  (---10---)(---20---)(---30---)(---40---)(---50---)(---60---)(---70---)(---80---)1234
-

  (---10---)(---20---)(---30---)(---40---)(---50---)(---60---)(---70---)(---80---)123
When I switch from Chrome to Firefox, it looks like the 84-character line doesn't scroll anymore. In general, I bet it depends on the browser's default fonts and such.

-----

1 point by akkartik 4895 days ago | link | parent | on: Multi-word commands

Yes, that feels like a very complete summary of the tradeoffs. I'm surprised I hadn't noticed that what I was doing was similar to the loop macro!

-----

1 point by akkartik 4897 days ago | link | parent | on: Update on Nulan

Ah, I'd wondered about that on firefox. I didn't see any discussion so thought perhaps one of my extensions was acting up.

-----

1 point by akkartik 4898 days ago | link | parent | on: Multi-word commands

"Why not just use actual pattern matching for the function arguments, like Nulan does? You can still use function-name-based lookup for calls. Then it would only be the arguments of the function that are pattern matched."

Yeah, the combination of permitting arbitrary things in function position and haskell-style pattern-matching allows us to emulate full-scale pattern matching. I hadn't noticed that before. I wonder if there's a cleaner way to integrate it completely with the operator-precedence parser.

-----

1 point by Pauan 4898 days ago | link

"I wonder if there's a cleaner way to integrate it completely with the operator-precedence parser."

I assume you're talking about Nulan since as far as I know, wart doesn't have a full-blown parser. In which case it works just fine in Nulan, because the parser runs before everything else (including macros).

-----

1 point by akkartik 4897 days ago | link

Yes, Nulan or something near it.

Patterns aren't first-class in haskell. Are they first-class in Nulan? If not, I was thinking aloud that Nulan's parser might be able to subsume patterns somehow.

-----

2 points by Pauan 4897 days ago | link

"Are they first-class in Nulan?"

They might be, once I make them user-customizable. At least, as "first-class" as macros.

---

"If not, I was thinking aloud that Nulan's parser might be able to subsume patterns somehow"

Well, the way I designed it, Nulan's parser is stand-alone: you can run it without any of the other Nulan stuff.

That's because the parser is really very simple. It's only concerned about lists, symbols, numbers, and strings. It has no knowledge of boxes, or macros, or anything like that.

This is by design. Although it would be possible to integrate the parser more closely into Nulan, I like the clean separation of concerns.

Patterns are currently executed during the macro expansion phase, so that's already long after the parser has run. In addition, while the parser operates everywhere, patterns can only be used in certain places.

How they work is pretty simple. Using the not-yet-created $pattern-rule macro:

  $pattern-rule foo -> {_ pat} val body
    'val + pat + body
Let's look at this program:

  (-> (foo 5) 10) 20
We're creating a function "-> (foo 5) 10" and then calling it with the argument 20.

Now, it will call the "foo" pattern with the following arguments: (foo 5), a unique variable, and 10. These represent the pattern, the argument to the function, and the body, respectively. Whatever the pattern returns is used as the body of the function.

After running the pattern, the end result is this:

  (-> u (u + 5 + 10)) 20
Let's look at another custom pattern:

  $pattern-rule and -> {_ @pat} val body
    pat.reduce
      -> x y
        pattern-match y val x
      body
This time we're calling the "pattern-match" function, which allows the pattern matching to be recursive.

Using this system, it's easy to do, for instance, list destructuring:

  $pattern-rule list -> {_ @pat} val body
    w/box i = -1
      pat.reduce
        -> x y
          pattern-match y ('val[,(++ i)]) x
        body
And now this program:

  -> (list a b c)
    prn a b c
Will get translated into this:

  -> u
    | box a = u.0
    | box b = u.1
    | box c = u.2
    | prn a b c
Though, the above "list" pattern doesn't support the @ splicing syntax, which is much more complicated to implement.

-----

1 point by akkartik 4898 days ago | link | parent | on: Multi-word commands

Thanks. It sounds like the basic question is: is selective evaluation useful enough to merit using up quotes inside param lists? Because if we didn't have it, we'd be able to use quotes for pattern matching.

-----

1 point by Pauan 4898 days ago | link

I'm not sure what you mean. Wart already uses macro-like vaus so it already uses implicit evaluation rather than explicit like in Kernel. And what else could quote mean in an argument list other than to distinguish variables from literal symbols?

-----

1 point by akkartik 4898 days ago | link | parent | on: Update on Nulan

Awesome!

a) if-box is a kinda contrived example. Perhaps we can find something better?

b) What's the difference between w/uniq and w/box? Both seem to create boxes. Could I just say this at http://github.com/Pauan/nulan/blob/5dfddacfbb/nulan.html#L44...?

  $mac if-box ->
    w/uniq temp
      'w/box temp = 5
         if temp
           temp + 1
c) I tried running this and got the following:

  TypeError: Cannot call method 'chunkSize' of undefined
I'm gonna play with this more!

-----

2 points by Pauan 4898 days ago | link

"if-box is a kinda contrived example. Perhaps we can find something better?"

If you have a better suggestion, I'm all ears. But I settled on that because A) it's simple, B) it's short, C) it's something that does actually come up in practice, so it's not too contrived.

---

"What's the difference between w/uniq and w/box? Both seem to create boxes."

Well, to explain this... unique variables are actually implemented as anonymous boxes. So implementation-wise, there's not much difference.

The difference is in the actual macros "w/box" and "w/uniq". Here's the definition for them:

  $mac w/box -> @args body
    'w/new-scope
       | var ,@args
       | body

  $mac w/uniq -> @args body
    'w/box ,@(args.map -> x 'x = make-uniq;)
       body
In other words, these are the same:

  w/box foo = 5
    foo

  w/new-scope
    | var foo = 5
    | foo
And these are the same:

  w/uniq foo
    foo

  w/box foo = make-uniq;
    foo

  w/new-scope
    | var foo = make-uniq;
    | foo
In other words, "w/box" puts a value into a box. "w/uniq" puts a box into a box.

They serve exactly the same purpose as "let" and "w/uniq" in Arc.

If you're asking why you can't just do this:

  $mac if-box ->
    w/box temp
      'w/box temp = 5
         if temp
           temp + 1
The answer is that "quote" inserts boxes for global symbols, but values for local symbols. That is, this macro...

  $mac foo ->
    w/box bar = 5
      'bar + 2
...returns "5 + 2", not "#<box bar> + 2". This is an unfortunate inconsistency which happens because I'm using JavaScript variables for the sake of speed.

I thought about having it throw an error for local symbols, which would have required you to write the macro like this:

  $mac foo ->
    w/box bar = 5
      ',bar + 2
But I decided that it wasn't worth it. Now that I think about it, I wonder if it would be possible to hack something up so you no longer need w/uniq...

---

"I tried running this and got the following"

It works fine for me. And in fact, the code snippet you posted seems exactly the same as what is already in the tutorial. Maybe you intended to paste something else?

As for the error... yeah, there are still some cryptic errors. I plan to make them more meaningful later.

-----

1 point by Pauan 4898 days ago | link

"Now that I think about it, I wonder if it would be possible to hack something up so you no longer need w/uniq..."

After thinking about it some more, I realized it won't work very well. It would require me to make all variables global, which would slow things down and be more verbose.

-----

1 point by Pauan 4897 days ago | link

Whoops, those examples should use "box" rather than "var". But otherwise they're correct.

-----


I've learned through bitter experience to treat all C warnings as errors, and more. The presence of a single uninitialized local variable somewhere in your program makes the entire program undefined. Where undefined means "segfaults in an entirely random place."

-----

1 point by nburns 4901 days ago | link

I think that's a good practice in general. But when you are experimenting and debugging, it can be useful to eliminate chunks of code by expedient means, which often generates warnings that you don't care about.

-----

2 points by akkartik 4898 days ago | link

I find programming to fractally involve debugging all the time. So if I allowed warnings when debugging I'd be dead :)

You're right that there are exceptions. I think of warnings as something to indulge in in the short term. The extreme short-term; I try very hard not to ever commit a patch that causes warnings. It really isn't that hard in the moment, and the cost rises steeply thereafter.

Incidentally, I'm only this draconian with C/C++. Given their utterly insane notions of undefined behavior I think it behooves us to stay where the light shines brightest. Whether we agree with individual warning types or not, it's easier to just say no.

But with other languages, converting errors to warnings is a good thing in general. Go, for example, goes overboard by not permitting one to define unused variables.

-----

3 points by akkartik 4908 days ago | link | parent | on: An editor for Scheme9

rocketnia, you'll like this make-your-adventure framework that comes with Scheme9 :)

http://www.t3x.org/s9fes/adventure.adv.html

-----


With regard to your trailing-paren hack at http://subjectivelisp.org/index.php?title=How_code_is_organi... -- I'm curious to get your reaction to my toy interpreter: http://github.com/akkartik/wart/blob/190ced3d73/004optional_...

-----

2 points by Pauan 4908 days ago | link

Second shameless plug: I've changed Nulan significantly since I last showed it off here. Here's a page describing Nulan's syntax system, which I believe is superior to all existing Lisp syntax systems:

https://github.com/Pauan/nulan/blob/79ea2a9fee8cb1ea7640d8f5...

-----

1 point by akkartik 4908 days ago | link

Very cool!

If I follow everything on that page, this allows you to programmatically control where lists are segmented, and to generalize infix, resolving function/macro calls to be anywhere in the list. In other words, macro foo doesn't have to look like

  (foo ...)
it can also look like

  (... foo ...)
Does this make the grammar context-sensitive? Does it often introduce ambiguity? Have you run into non-terminating parsing? Can you talk about how it compares with oMeta? (Are you sure you aren't greenspunning it? :)

-----

2 points by Pauan 4908 days ago | link

"If I follow everything on that page, this allows you to programmatically control where lists are segmented [...]"

Something like that, yes.

---

"[...] resolving function/macro calls to be anywhere in the list. In other words, macro foo doesn't have to look like"

Not quite. The parser is very simple: it just pushes symbols around. Which means that when you use a syntax rule like this:

  $syntax-infix "foo"
Then the parser will rewrite this:

  1 foo 2
Into this:

  foo 1 2
This all happens before macros are expanded, so macros receive a uniform list representation. In this way, it's similar to wart's system, except that it's much more flexible and powerful.

---

"Does this make the grammar context-sensitive? Does it often introduce ambiguity? Have you run into non-terminating parsing?"

I have no idea, no, and no. It's all just simple list transformations, similar to what macros do.

---

"Can you talk about how it compares with oMeta? (Are you sure you aren't greenspunning it? :)"

I haven't use oMeta, but from what I understand, it uses something like PEG parsing, which is completely different.

Think of my system as being like macros: you take this list of symbols and that list of symbols and return this list of symbols.

The difference with macros is that my system has left/right associative, prefix/infix/suffix, precedence, and a slew of other options too.

The key insight is that unlike most syntax systems which return a single expression, Nulan's syntax system returns a list of expressions.

And then the syntax rules operate on this list, which effectively lets them look-behind and look-ahead arbitrarily many tokens, but only within the list.

PEG parsing lets you look-ahead as many tokens as you like, but not look-behind. Nulan's system supports both, but the amount of look-ahead/behind is controlled by the indentation, so everything is handled in a consistent way.

-----

2 points by Pauan 4908 days ago | link

To fully explain how the system works, let's use this program:

  prn 10 20
    foo bar + 50 * 20 - 30
After tokenizing, we use the significant whitespace rules to wrap the stuff in lists:

  {prn 10 20
    {foo bar + 50 * 20 - 30}}
Now, for each list, we run a modified Pratt parser on it:

https://github.com/Pauan/nulan/blob/79ea2a9fee8cb1ea7640d8f5...

Yes the Pratt parser really is that tiny. Now, let's start with this list:

  {foo bar + 50 * 20 - 30}
We start parsing at a precedence level of 0. First, we take all the non-syntax rule symbols and put them into a list. Now we have these two lists:

  left:      {foo bar}
  symbol:    +
  remaining: {50 * 20 - 30}
The + syntax rule has a precedence of 70. Now we need to recursively call the Pratt parser with a precedence of 70:

  left:      {50}
  symbol:    *
  remaining: {20 - 30}
The * syntax rule has a precedence of 80, which is greater than 70, so it continues parsing:

  left:      {50}
  symbol:    *
  right:     {20}
  remaining: {- 30}
The - syntax rule has a precedence of 70, which is equal to 70, so it stops parsing. It now calls the action function for * with the arguments left, symbol, and right, which returns {* 50 20}

Now we go back to the + syntax rule, which looks like this:

  left:      {foo bar}
  symbol:    +
  right:     {* 50 20}
  remaining: {- 30}
It calls the action function for + with the arguments left, symbol, and right, which returns {foo {+ bar {* 50 20}}}:

  left:      {foo {+ bar {* 50 20}}}
  remaining: {- 30}
Now it continues parsing with a precedence of 0. - has a precedence of 70 which is greater than 0, so it recursively calls the parser with a precedence of 70:

  left:      {foo {+ bar {* 50 20}}}
  symbol:    -
  right:     {30}
  remaining: {}
It now calls the action function for - which returns {foo {- {+ bar {* 50 20}} 30}}

We now call the Pratt parser on the outer list with a precedence of 0:

  left:      {prn 10 20 {foo {- {+ bar {* 50 20}} 30}}}
  remaining: {}
There aren't any syntax rules, so it just returns it unmodified, and so the final answer is:

  {prn 10 20
    {foo {- {+ bar {* 50 20}} 30}}}
---

Pratt parsers are absolutely amazing. They're very very short, easy to implement, very fast, and incredibly flexible.

If you want to learn more, I recommend these links:

http://eli.thegreenplace.net/2010/01/02/top-down-operator-pr...

http://javascript.crockford.com/tdop/tdop.html

http://effbot.org/zone/simple-top-down-parsing.htm

https://groups.google.com/forum/?fromgroups=#!topic/comp.pro...

http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-e...

I can also clarify things further, and provide a simple stripped-down version of Nulan's parser, if that helps to understand.

-----

2 points by Pauan 4907 days ago | link

I just made some changes to Nulan's parser. Now almost all the syntax is customizable:

https://github.com/Pauan/nulan/commit/b13e6477726cc111af4c23...

The changes are: renamed "action" to "parse", added in a "vertical" option, added in a "tokenize" function.

---

Now, significant whitespace is hardcoded, numbers are always 0-9, and symbols are anything that isn't a number or delimiter.

But everything else is customizable. Everything. Even comments, strings, and space/newline is customizable.

---

The new "tokenize" function lets you fairly easily add in new tokenizers, which is what string/comment/space/newline does.

---

The new "vertical" option is pretty cool too. It's currently only used by "|" and it means that this:

  foo bar
    | qux 1 2 3
    | corge 4 5 6
Is parsed like this:

  {foo bar
    | {qux 1 2 3
       corge 4 5 6}}
But the "|" syntax also specifies "separator", so it's further parsed to this:

  {foo bar
    | {{qux 1 2 3}
       {corge 4 5 6}}}
After running the "parse" functions, the final result is this:

  {foo bar
    {| {qux 1 2 3}
       {corge 4 5 6}}}
By the way, "|" is just like "do" in Arc, but I think it looks a lot nicer.

-----

1 point by akkartik 4908 days ago | link

Thanks! 'Pratt parsers' was precisely the phrase I was looking for :)

> > ..resolving function/macro calls to be anywhere in the list..

> Not quite.. This all happens before macros are expanded..

Yeah, that was exactly what I meant.

-----

More