At the end of the Arc tutorial I included a couple web app
hello-worlds. I'd greatly appreciate it if people familiar
with other languages could show how you'd translate this one:
Here's what it has to do. First generate a page with
an input field and a submit button. If the user clicks
on submit, he gets a second page with a link saying
"click here." If he clicks on that, he gets a third page
saying "you said: ..." where ... was whatever he put in
the input field. This has to happen without the value
being passed in the url; it should not be possible to change
the behavior of the third page by editing the url in
the second.
What is worth noting in the above post is the "With the PLT web server" part.
What I mean is most of the other posts demo how to do it with some required (exotic) framework. This example is just using the basic web server APIs of the PLT continuation based web server.
In other words in this example, Eli BOTH writes the framework (in the first 9 lines), then uses the framework in the last 4 lines.
PLT's mzscheme is in effect, a very distant cousin to Scheme. If they weren't all eggheads and had at least one attentive marketing person, they would have labeled it some new fangled SuperLispIncarnateNextGeneration (SLING) moniker and received a bit of buzz.
I haven't looked at exactly how PG implemented Arc over MzScheme, but consider this. Much of what he did _could_ be done using no more then PLT mzscheme's out of the box, macro system, custom language module capability and custom reader capability, by anyone. Yes, even you.
In some sense most of Arc is just what anyone does who develops in PLTs Scheme/laguage system, i.e., create their own custom language/DSL. PG's may have done it a bit better, more extensively, with a general purpose intent.
So what is the secret weapon here Arc, or the underlying system that allows someone to create an Arc so easily?
You could, right now, be using PLT MzScheme to do your own Arc(s), your own, just for you and your friends custom language/DSL.
Ruby, Python are cute, and deserve having a user base, but PLT mzscheme is without doubt, the best and most powerful language system out there that "no one talks about."
Thanks for the highly accurate description of PLT and PLT Scheme. I founded PLT as an academic but I am one who appreciates the necessity to open a channel of communication between the 'real' world and academia.
While Matthew and Robby are the "drivers" (with lots of co-pilots :-) I think that setting this tone early in the project has placed PLT Scheme naturally in a lonely niche: it is a real scripting language with capabilities that rival those of everything out there and it is also a serious academic infrastructure. Typed Scheme -- the first and only sound 'gradual typing' language so far -- is just an example of what I mean. We can publish about this in the flagship research conference on programming languages and at the same time, we are using it for its intended applications. Sam Tobin-Hochstadt is porting a part of DrScheme to Typed Scheme as I type. It is this kind of experiment -- porting a piece that your "life" depends on -- that puts us squarely on the applied side, too.
I'm currently in the process of teaching myself Scheme as well through a combination of HtDP and SICP and I was wondering what SIPS is? Perhaps it's another source I should check into.
Heh, that's funny and disappointing all at the same time--I was really hoping there was another really great resource on the web for learning Scheme. Thanks for the reply.
It's quite short and set out in a question and answers style. The thing is, it very gently and very subtlety bends your mind around to the Lisp way of thinking. After reading this, SICP seems so much more accessible.
There are also two follow on books - The Reasoned Schemer and The Seasoned Schemer which are again quite short and use the same interactive approach. I haven't had chance to read them yet but they get rave reviews on Amazon
Not really Arc related, but here you go in 3 steps:
1 - Paste the following code into a new document test.ss and save in servlet example folder (/usr/plt/collects/web-server/default-web-root/servlets/examples/test.ss)
If document name test.ss changes, change the module name on first line.
Let me first state that I'm happy that Arc's gotten this far and I wish it - and PG - all the best with its future: I hope it's a bright one.
That said, I must disagree with Paul's recent blog entry/essay and this challenge...
Brevity or compactness as a measure of a language's "greatness" is a red herring. There are two acceptable methods by which source code can be compact: syntactic sugar and factoring/libs. I say "acceptable", because I don't consider putting everything on 1 line or using function names like "f" acceptable. And while syntactic sugar can be good at times, it can also lead to write-only code (see: APL, Perl (imo), and many others).
There's absolutely nothing special with PG's example code and the challenge isn't much of a challenge at all. Analogy: everyone has cars and I "invent" a jet, and then challenged everyone else to a race from Boston to LA. I've picked a challenge to show off what I consider to be my best trait: speed. Why didn't I challenge everyone to get from my home to the local grocery store? Given the terrain, perhaps a mountain bike would have been the best tool for that job.
How about this challenge for Arc:
On a little-endian machine, read in the Quicktime .MOV header atom format (big-endian), parse, and dump it to the console in a human readable format.
The reason I say there's nothing special with the example code is that we all know there's a lot of code going on under the hood behind it - a lot of code that PG has already written. And it's a cop-out to state at the end of the challenge... "Code to import standard libraries doesn't count..."
The code still had to be written. It isn't magical just because it comes standard. It shouldn't make a difference if it's included with the language or downloaded from the internet. The question isn't how much code was written, but rather, how much code did I have to write?
Again compactness is a red herring.
Perhaps Arc will be my language of choice for implementing <insert task>. It would be far more informative to me to be told (by PG) what problem(s) Arc is being design to solve and then show me how my life as a programmer would be easier - using Arc - to accomplish those tasks.
Again, I wish PG all the best with Arc, and I look forward to using it. But currently, I very much agree with Ron Garret when he said, "...[Arc] seems to pretty much punt on all the hard problems of language design."
"The code still had to be written. It isn't magical just because it comes standard. It shouldn't make a difference if it's included with the language or downloaded from the internet. The question isn't how much code was written, but rather, how much code did I have to write?"
You're right. That's why libraries are so popular. Laziness pushes us to excell.
here's a little gem. "brevity is the soul of wit" - Shakespeare
Using Prototype and even more of a hack:
<script src="prototype.js"/><input id="a"/><input type="submit" onclick="h=Element.hide;h('a');h(this);u=Element.update;u('c','Click Here');"/><a id="c" onclick="u('c','You said '+$F('a'))"/>
Obviously I'd need to write three templates for the three pages.
This is a neat example, since it shows how terse Arc can be. I'd be interested to see how well this scales to a large site. For example, I normally like keeping my HTML and Python code separate, so that when I have thousands of lines of each, it's easier to sift through it all and find what I'm looking for. However, if my code were consistently this much shorter, then that wouldn't be as necessary.
no, direct accessibility by URL was not in original requirements, so i didn't bother with it. in ABCL-web there is one entry point and other pages are linked via action-link from it. so if i rename said to start-page it will work directly.
newer version of ABCL-web i'm currenly working on supports publishing functions by URLs, so i could write "(defpage said.." with it.
Technically it was in the original requirements, which were to translate the Arc code. Would there be a way to do that in ABCL? If not I look forward to seeing it in the new version.
everything is possible, of course, but there's no _pretty_ way to define new named entry point. with code I currently have at hands one needs to populate table manually:
The only parts of this that aren't standard in PHP are the sset() and sendpage() calls, which are short for some longer stuff involving a template object, and the localredirect(), which just does some bookkeeping.
This is a lot longer than the Arc example, of course. However, one advantage it has (which is a must for some of us) is that once I'm done writing it, I can hand the html files to a web designer and I don't have to do anything at all when the boss/client wants to completely change the way it looks.
As a person who works in a commercial environment, where code maintenance is more important than the generation of new code, looking at the above makes me want to never, ever, ever consider Arc for any development.
Writing short programs is nice, but looking at the above example, I can't make heads or tails out of it, or how it works. "Unfamiliarity with Arc's library!" you cry. Yes. Yes it is. But, I'm just as unfamiliar with other frameworks demonstrated in the comments, and I can follow those just fine (well, except the Perl and Ruby ones). The Python examples are _stellar_ in their comprehensibility, with ANSI CL coming in at a close 2nd place, and Smalltalk following in at a luke-warm 3rd place; there is sufficient redundancy in the code that, while being short and sweet, it still gave enough context to know and understand how the pieces fit together.
Not so with Arc. It's TOO terse. This is the same problem that you see with otherwise stellar languages like J/APL, Perl-golf, and older Linux and BSD source code files. Yes, your productivity sky-rockets when you write the code. Alas, your productivity sinks like a rock while trying to maintain it, unless you've been at it for at least 3 years.
This language is not for me. The cost-benefit ratio is not sufficient to make me jump ship.
Really? I could read it just fine, and I've never used Arc before. There was a bit of a learning curve when I was figuring out what the library functions did, but that took about 15 seconds -- roughly the same time it took to figure out exactly what was happening in the Python and CL examples.
Your understanding problem can probably be traced to two things: the implicit separation of the code into different pages (you haven't used the library yet, so you don't know what visual cues to look for), and the backwards order of the pages from the structure of the aform and w/link macros. Both problems can be solved by using Arc's library for an hour or two: you'll either get used to Arc's library or you'll find a way of handling common cases that's more to your taste.
In this case, I suspect the code could be made a lot clearer to you by splitting it into separate page-maker functions for each of the pages that the user will see. This would add about three lines of trivial code, assuming that the library works the way I think it does.
Here's a first draft in Rails that doesn't use any template files.
class HomeController < ApplicationController
def first
if request.get?
aform 'first', [input('foo'), submit]
else
session[:foo] = params[:foo]
wlink 'third', 'click here'
end
end
def third
pr "you said: #{session[:foo]}"
end
end
A small example is fine, but wouldn't it be better if it at least did some basic validation? How does the Arc example change if you enforce only alphanumeric characters in the input field?
class HomeController < ApplicationController
def first
if request.get?
aform 'first', [input('foo'), submit]
else
if params[:foo] && params[:foo] =~ /[A-Za-z0-9]/
session[:foo] = params[:foo]
wlink 'third', 'click here'
else
aform 'first', ["Please enter an alphanumeric string", input('foo'), submit]
end
end
end
def third
pr "you said: #{session[:foo]}"
end
end
Of course, we'll then want to allow the designers to modify the presentation, and allow the copy writers to add compelling text, etc. So, it seems like a template system is the way to go, but maybe someone has a better idea.
I can see pros/cons of a template based approach vs. generating everything. I do find the separation of templates from code to be very beneficial since I haven't been able to get to the point of controlling 100% of the presentation via CSS alone - sometimes a simple structural change in a template file is less intrusive than modifying code.
Also, I wonder about the overhead of continuation based approaches with higher volumes.
Most of the examples posted so far fail in a fairly big way -- they break if you use the "said" page in more than one tab. Pretty much all of the examples that use cookies have this problem.
This might seem like a minor thing, but it ends up being pretty important in practice. E.g. most airline fare comparison sites don't allow you to do multiple searches in parallel; most checkout processes get very unhappy if you use the back button. The basic browsing metaphor -- of a page's state being contained within that page -- is broken by cookies, and it shows even in these toy examples.
(Sometimes breaking that metaphor is ok -- e.g. for persistent logins.)
With the closure-based approach, you don't have to worry about tabs, or the back button, or any other issues caused by badly leaking abstractions.
I think cookies vs closures is mixing apples and oranges. First, if you're using server-side state, the session ID can be stored in a hidden field (post) or URL (get) as Arc does, or the session ID can be stored in a cookie. The cookie behavior can be a bug or a feature depending on what you're doing; for example, you probably don't want two different tabs to have two different shopping carts and two sets of user information. Session ID in the URL has the disadvantages of ugly URLs ("sessionid=line noise"), lack of persistence, difficulty with bookmarking, and SEO negatives. Web frameworks (JSP, ASP) typically support either model.
Second, improving back button behavior is a matter of setting all the right nocache attributes. If you use forms, you're likely to have trouble with back navigation no matter what you do ("The page contains expired POST data").
Finally, I don't see how closures have any impact on the user experience one way or another, since it's just a different way of storing server-side state. The server can be implemented with closures, in-memory state, state backed by a database, or trained pigeons and it shouldn't make any difference to the user. (Modulo performance, reliability, etc of course.)
Am I missing something about how closures solve tabs and all other leaky abstraction issues?
"I think cookies vs closures is mixing apples and oranges"
In theory, that's absolutely true; of course there's no technical reason that one can't use cookies and still keep the desirable properties of the Arc example (outlined in the grandparent).
But that's really the point: even though both styles make either model _possible_, they still encourage radically different approaches -- and this is of course borne out by the stuff people have submitted.
Secondly, on the perceived cookies/closures dichotomy, you reduce things to a question of where one should store the session ID, and that's a totally orthogonal issue.
We basically have three models -- sessions with IDs in cookies, sessions with IDs in URLs, and closures. Cookies give you a single, linear progression of state; session-IDs-in-URLs give you multiple linear progressions; and closures give you a nonlinear tree of progressions.
What do I mean here?
Sessions with IDs in cookies are simple: they're a single global state. Session IDs in the URL are a small bit closer to the closure-based approach, but they really just mean that the flow of state proceeds linearly in several independent threads. It's still a long way from matching the browser metaphor of "a page's state being contained within that page".
With session IDs in the URL, you can't get identical semantics to the closure-based approach unless you do make your sessions immutable, and create a new clone at each juncture. And if you do adopt this "session frame" approach (should that be "stack frame" approach?) you've basically created succumbed to some form of Greenspun's Tenth Rule.
Does this really matter? When state is contained within the page -- that is to say, when the closure-based approach is used -- it makes for much more flexible browsing. To take a tangible example, say you're searching for an airfare. You first search for tickets from SFO to BOS, then on the next page pick dates, and then on the third you (holding down alt) open a new tab investigating the price when you book first-class, while you proceed with economy in your main tab. You go through a few more pages, and you're now at the checkout in both. But you realise that you really need to go a day earlier, and that first-class is too expensive, so you close that tab, and hit back 'till your at the date selection page, and then head back to the checkout.
And it all just works. We humans are inquisitive creatures, and the tree of closures facilitates our natural instinct to poke and then retreat when things don't look right. Desktop app designers have long known this (even the first Macintosh had undo), and though generally far less information-dense, almost any modern desktop app supports virtually-infinite undo. Session-based approaches on the web choke this.
If the airline example sounds a bit contrived, it's because it is -- but at the same time, it's also a lot less chaotic than many people's flows when going through this an airline booking process (I know that for a fact because I spent a while investigating it once...).
The biggest difference between my example and real life is that, in mine, things _work_, whereas in real life, opening the new tab or using the back button would almost certainly completely screw the web-app up.
As heavy internet users, we've grown to accept this crap, because 1) we're used to it and 2) we know it's hard to get right. But it's not inevitable. Browsers can still be made to work as advertised.
This response has been hurried; I should probably make it into properly-written blog post or something.
Another option is to have all necessary information about the current operation in the URL. This is highly scalable, since you don't need to keep track of anything user-specific on the server(s), and the navigation supports branching and back/undo just like you describe.
Strangely enough PG specifically disallow this approach in his competition!
Most web apps need _both_ global session state and URL-based state. As others have pointed out, if you browse a product catalog, you would like to be able to branch into different browser windows or use the back-button. However, when you add an item to the shopping basket, you want it to be a global state change (you want have the same shopping basket in all windows), and you don't want a buy to be undone by clicking back.
Continuations are only an options for handling URL-based state, not for handling global state. And for page state they have some limitations.
For example, if all navigation is handled by continuations, you basically have to store a continuation for every hit indefinitely, since you dont know if the user have bookmarked the URL. If you don't want to store the continuations forever, you should only use them on pages that are not bookmarkable anyway, i.e. pages that are the response to form posts. But then the stated advantages, like the ability to branch and use the back button is moot, since you cannot do that anyway with form responses.
Continuations are really nifty for quick prototypes of web apps, but for production use, I believe they are a leaky abstraction.
I think you're spot on. I just wanted to share a little trick, with regards to:
Second, improving back button behavior is a matter of
setting all the right nocache attributes. If you use forms,
you're likely to have trouble with back navigation no matter
what you do ("The page contains expired POST data").
The key is to use a 302 redirect immediately after a successful post rather than a 200. This makes using the back button take you back to the form, rather than trying to POST it again.
On the other hand, I find the ability to resubmit forms with the back button very useful at times, so I'm not sure this is always the right thing to do. But it's a neat trick.
#!/usr/bin/env ruby
require "ramaze"
class MainController < Ramaze::Controller
def index
if f = session['foo'] then "you said #{f}"
elsif session['foo'] = request['foo'] then A("click Here", :href => '/')
else '<form><input name="foo" /><input type="submit"></form>'
end
end
end
Ramaze.start :port => 7001
__END__
Using Ramaze[http://ramaze.net]. Though I do find it kinda unfair comparing languages over web frameworks... Rails.. shudder
> Though I do find it kinda unfair comparing languages over web frameworks...
My sentiments exactly. This is a demonstration of a library, which handles a certain scenario very well. I'm sure, an equally terse library could be written in most other languages.
Coincidentally, in the few cases, where I need application state stored at the server side, I usually want it to be global. If I have the same site open in multiple tabs, I actually want the shopping cart from window one to reflect the same state as the shopping cart from window two. But then again. I guess that is a matter of me failing to see the point of continuations, and therefore not overly relevant to the topic.
Typically, you'd never write the program that way in Ruby. All the popular ruby web frameworks are not continuation or closure based. Instead, you'd keep the state in memory on the server, tied to the session. You'd also use three templates, one per page. In Rails:
def said
if request.method == :post
session[:said] = params[:said]
render :action => "clickhere"
else
render :action => "result" if session[:said]
end
end
default template said.rhtml:
<% form_tag do %><%= text_field_tag "said", "" %><%= submit_tag %><% end %>
clickhere.rhtml:
<%= link_to "click here", "" %>
result.rhtml:
You said <%= session[:said] %>
But now that you mention it, ruby has callcc...let me see what that implies...
Neat, thanks. (Not quite "right" since I can change &foo=myinput on page 2, but I'm guessing that could easily be fixed with an extra closure somewhere).
I don't have fancy HTML-generation w/callbacks, but here goes:
#!/usr/bin/perl
use Continuity;
Continuity->new->loop; # This starts the webserver
sub main {
my $request = shift;
$request->print("<form><input type=text name=foo><input type=submit>");
my $foo = $request->next->param('foo');
$request->print("<a href='.'>Click Here</a>");
$request->next->print("You said: $foo");
}
Using Catalyst, a Perl version might look like this:
package ArcChallenge;
use strict;
use Catalyst;
use Catalyst::Action::REST;
my @said;
sub index : Action ActionClass('REST') {}
sub index_GET {
my ($self, $c) = @_;
$c->res->body( "<form method=post><input name=said><input type=submit>" );
}
sub index_POST {
my ($self, $c) = @_;
my $n = push @said, $c->req->params->{said};
$c->res->body( "<a href='/said/$n'>Click Here</a>" );
}
sub said : Regex('^said/(\d+)$') {
my ($self, $c) = @_;
$c->res->body( $said[ $c->req->captures->[0] - 1 ] );
}
__PACKAGE__->setup;
1;
Bit clunky as yet, but work's underway to tersen up the syntax. (For anyone interested in how this will be implemented under the hood, the magic CPAN incantation is Devel::Declare. However it's a months-old work in progress so docs are minimal.) Once done it will remove most of the repeated boilerplate bits in the above code (eg. the assignments from @_ to unpack the parameters).
package CatArc;
use strict;
use warnings;
use Catalyst qw/Session Session::Store::FastMmap Session::State::Cookie/;
our $VERSION = '3.14159265359';
__PACKAGE__->setup;
sub index : Index { } # No need to do anything
sub landing : Local {
my ( $self, $c ) = @_;
$c->flash->{said} = $c->req->params->{said};
}
sub display : Local { } # Do nothing
sub end : ActionClass('RenderView') { } # Do a magical nothing.
1;
Plus templates, for crying out loud. They exist for a reason. TT used for the sake of "everyone knows it": index.tt and landing.tt are as good as static, containing just a form and a link resp. display.tt contains "You said: [% c.flash.said %]".
Oh yeah. A little side note here. Just wondering how you guys want to judge this submission for length since:
1) No programming languages used, only markup/presentation languages (0 lines of code?).
2) Entirely client side behavior.
3) Works simultaneously across multiple page instances in tabbed browsing as several folks have pointed out that this could be a problem with some of the session managed solutions here.
Anyway, I'm loving everyone's submissions. These are some great hacks, folks. Keep'em coming. &P
Excellent submission. Didn't think it was possible to deal with that only through pure HTML... As for length, well, no matter whether it is pure markup language only on client side or not, the point is, to make it work, you had to type n lines of code, and pg's motto is : "the shorter the better"
Ah, touche. However, pg claimed that the length of the program should be measured in terms of the length of its parse tree, not the number of lines of code. In everyone's submissions here, their code is generating one or more webpages in HTML (as, of course, is pg's). The parse tree being measured (at least in pg's original arc example) appears to be that of the server-side language used to do the scripting and generate the HTML and does not include the parse tree of the HTML itself, which is, of course, generated and executed in the client-side webbrowser for everyone's example and shouldn't be counted toward the server-side parse tree.
So then, to sum it all up, I'd have to say that this "program" to meet pg's requirements requires no server-side code (and thus a server-side parse tree of size 0) since it's just being served up as a static page. If you want to count the DOM parse tree length of my HTML/CSS, then you must also count that generated by every other submission here, which essentially adds some constant factor to all the metrics in this forum and thus becomes essentially a non-useful piece of information in comparing the entries.
pg claimed that you don't need to include your template libraries or other magical exotic web framework code. You can just assume it's there, so most folks aren't showing their HTML, and of course, it wouldn't make sense to count it towards their program's parse trees on a tag-by-tag basis since it's likely just a bunch of strings in most of their systems. But hey, like I said, in the end they're all making webpages, and if that's the case then I still think I've got a bit of a headstart on a lot of folks.
Then again, maybe not. What do you folks think? Am I just a big fat cheater or what? prepares to dodge ballistic tomatoes
Does it really matter where the code runs? Or does it just matter who writes it?
In most examples, HTML runs on client machines which may be roughly comparable to your example, but who cares, because the code (whether measured in lines or nodes) the author had to write to generate that HTML was presumably superior in some way compared to writing HTML manually. (Otherwise why not just code straight HTML, all the time?)
In your example, even though the server side code consisted of 0 lines/nodes, the code written by you consisted on an entire HTML page. So did you really save any time or effort in writing the HTML? Maybe, that's why we compare the code trees. But even in that case, the HTML itself will count toward the code tree.
Perhaps this means that HTML templates (not HTML generated by server-side scripts) used in other entries should also be included in their code tree count, if they had to be coded manually by the author. But even if this is the case, it doesn't just add a constant factor to all entries (I believe the arc entry did not require any HTML to be written by the author), and thus is still useful information, although perhaps the two should be considered separately.
P.S. Really liked your submission. I just don't think it counts as 0 lines of code.
Thanks, eds. I think that's a pretty fair judgment, and you're right that it wouldn't be a constant factor because different people are generating different amounts of HTML in the end. My example only has one page, and though it should be valid HTML4, I do include some CSS and div's that other folks wouldn't need. Admittedly, to keep theirs valid, they'd also need a lot more html, head, title, and body tags than me if they're making multiple pages.
The thing that strikes me as being kind of a funny metric here with code length is that if the author wrote the HTML and imported it into their code, you have to count it, but if some other author wrote the HTML and you import it, then you don't have to count it. Seems kind of weird to me, seeing as this opens the door to somebody else saying:
Alright, maybe gjohnson's code count includes all that silly HTML and CSS, but my program uses his HTML template file (which isn't much of a template in this case, of course, being kind of the whole shebang), and has a parse tree of length 1.
Here it is:
<!--#include virtual="gjohnsons_magic.html" -->
Tada! I mean, heck. It's true that pg didn't write the HTML in his arc challenge submission code, but wait a minute! He did write it in his function library when he was defining the language. So being the author of the original HTML, does he have to include it after all?
You're right. That was one of the main objection against this challenge, from what I read on many forums. Well, the really convincing test is to use Arc for real. I wrote a small webapp with Arc and I never wrote such an app so fast. Quite amazing for a language I didn't know.
Point taken. This stuff about parse trees and who-wrote-what is after all just an academic argument. You're absolutely right that it's what we can write and how he enables us to write it in the end that makes this stuff all so interesting.
Anyway, keep going everyone. Those submissions are rockin' on. I've got my mind wrapped around a pretty perversely heinous challenge response that I may get to in the next couple of days. Keep your eyes peeled!
P.S. Did anybody notice that those class="page" attributes are unnecessary in my HTML/CSS code above? Just checking. Looks like I forgot to remove'em before posting.
The actions could be lambdas to make it a closer translation, but I probably wouldn't. The library uses 'greenlet' for the continuation-y functionality.
What happens in Arc in more complex examples if I re-click a state-changing-link that's already been clicked?
Nice idea, but it doesn't work. It should be something like this:
renderContentOn: html
said
ifNil:
[html form: [html textInput callback: [:v | said := v].
html submitButton]]
ifNotNil:
[clicked
ifNil: [html anchor callback: [clicked := true]; with: 'click here']
ifNotNil: [html text: 'You said: ', said]].
A more elegant implementation might use the lower levels of Seaside directly, and dispense with components completely. But Arc is about brevity, not elegance, right?
Well, even that code omits the definition of "said" and "clicked". (The latter is unnecessary as far as I can see.)
I agree that mine isn't precisely by the spec -- you have to press 'enter' to submit the form, and there's an ok button on the last page -- but these are fairly trivial differences in presentation due to the behaviour of the built-in libraries.
(also, minor footnote: I'm assuming that "foo", "said" and "clicked" are instance variables in each example.)
Interestingly, you can control which binding is passed to which transaction. Let's say you wanted to show the form twice, and display the answers in reverse order. So something like this:
Display the form to collect answer1 (still named foo), on submit display the form again to collect answer2 (also named foo), then the "click here" page, then a page with "you said: [answer2]" and a "click here" link, then a page with "you said: [answer1]".
How do you do that in Arc? In this Ruby example, it would be:
I'm more of a designer than a developer, and when looking at the Arc example I have to ask how this matters in a real world example. How much extra code would it be to add classes on the links or form elements so they could be appropriately styled with CSS? The Arc code is short because it drops in HTML and nothing else, but that's not useful at all when actually creating and producing web applications.
Could someone rewrite the original Arc code with a form that has an ID, labels in the form for accessibility, and buttons & links with classes?
While it's not as concise as the Arc solution, it does have some advantages. First, it satisfies the requirement of returning the user's input, even for accented characters. Second, it doesn't pass stuff in the URL like the Arc solution. Third, it plugs the obvious XSS hole. Fourth, it produces valid HTML. (I picked XHTML for maximum pain :-)
Paul, all you have to tell people who are yelling that Arc doesn't have X: "Yes, you're right, it doesn't have that. Arc's at an early stage, just a preview of the main idea behind the language. Some of these problems, yes, they haven't been solved yet. Besides, I could have just decided not to release it." One can't argue with that, unless one's a jackass.
Instead, you aren't saying much besides "I'm right, you're wrong." Which is an arguable premise.
I apologize; `hasIndex` is a confusing name -- I was thinking of the `ISINDEX` html tag (it creates an html form with a single input field).
The idea is that `hasIndex f` creates a page that prompts for a string, then (after the page is submitted) calls the function f with the input string as parameter and shows the resulting page.
By the way, \ is just the Haskell syntax for lambda -- hey, whaddayaknow, it's one character shorter than `fn` :-)
I do agree that small samples can be instructive when comparing languages (although I'm getting tired of seeing quicksort in Haskell :), but I think some slight tweaks would make this much more compelling:
1) Some simple validation on the input field. Ensure something was entered, and ensure it conforms to some pattern. Notify the user of invalid input and request it to be reentered instead of going to the second page.
2) Some facility for placement/presentation. If you're depending totally on CSS for presentation, that's fine, but at least give the poor designer some ids/names to work with.
Having said that, I do think it's a great example of conciseness, and it has caused me to think about adding some Ruby functions to make my Rails development more concise. Not quite ready for continuations though.
The only difference is that INFO-MESSAGE has an ok button rather than a link.. if i wanted, a WITH-LINK macro is quite easy, but it's not included in the base LoL .. yet...
The arc version lacks the conditional... and info-message is not quite just a link. So, assuming a new standard-component MESSAGE we could simply have:
I would never write UCW/LoL code like this.. it's not at all in the style of a UCW application.. but the challenge was to use what was available in the language.
UCW is component based by default, so quite verbose for a small program like this indeed. It's not hello-world optimized. Having said that, there are a number of things we could do to optimize things for this particular example. It's all lisp right?
;;;; Still using components.
;;;; This, while it meets the spec, is semantically quite
;;;; differrent than the arc version, IIUC.
(defvar *is-link* nil)
(defun/cc pr (message args &optional (linkp *is-link*))
(apply 'call-component *component* 'message
:message (apply 'format nil message args)
:linkp *is-link*))
(defmacro w/lnk (&body body)
`(let ((*is-link* t)) ,@body))
(defmacro aform (action &body body)
`(<lol:form :action ,action ,@body))
(define-symbol-macro _ (message *component*))
(defcomponent message (info-message) ()
((is-link? :accessor link-p :initarg :linkp))
(:render :around ((m message))
(if (linkp m)
(<lol:a :action (answer m) (call-next-method))
(call-next-method))))
;;;; finally:
(defcomponent said (message) ()
(:render ((said said))
(aform (progn (w/lnk (pr "click here")) (pr "you said ~A" _))
(<lol:input :accessor _)
(<:submit))))
That meets the spec, and the code itself looks very similar, but it's not at all doing what the arc version is doing. UCW does its rendering via components by default, hence my calls to MESSAGE every time i wanted to output. This is really cheating, and would require a lot of changes to keep up with further examples in arc.
As luck would have it, UCW is really quite flexible. In arc, we're just using call/cc directly, without the component architecture present in other continuation based frameworks (seaside, ucw. weblocks). It is trivial to remove the component stack and just render the continuations directly.
;;;; * LoL arc
;;;; This is a minimal 'arc-like' framework build on UCW.
(defclass arc-action (basic-action)
((renderer :initarg :body))
(:metaclass action-class))
(defmethod render ((action arc-action))
(funcall (slot-value action renderer))
(defmethod call-render ((action basic-action)) a s f)
(handle-raw-request (:with-network-output *standard-output*)
(render action)))
(defmacro defop (name &body renderer)
`(progn
(defclass ,name (arc-action) ()
(:metaclass action-class)
(:default-initargs :body (lambda () ,renderer)))))
(defun arg (name)
(get-parameter (context.request *context*) name))
(defmacro w/lnk (action &rest txt)
`(<lol:a :action ,action :action-class arc-action (<:as-html txt))
(defmacro aform (action &body body)
`(<lol:form :action ,action :action-class arc-action ,@body))
(defun input (name &optional value)
(<:input :name name :value value))
(defun submit () (<:submit))
(defun pr (&rest strings)
(print (apply 'concatenate 'string 'strings))
;;;; * Finally, we can re-create the arc example in CL
;;;; using UCW/LoL. I'm not sure what 'req and '_ are
;;;; are for in the original, so i've omitted them.
(defop said
(aform (w/lnk (pr "you said: " (arg "foo"))
(pr "click here"))
(input "foo")
(submit))
w00t! I do believe that is shorter than the ARC version :).
EDIT: oops .. figured out what the '_ is doing and it's absolutely needed. In Arc we are using a closure to capture the state of the world. In CL, special variables and the lack of [] make that a little more difficult/verbose. Rather than re-write my AFORM and either make a [] reader macro or code-walk AFORM, i'll just cheat a little and use an object to store params.
Objects and Closures .. same thing right?
;;;;We need to add a slot to the 'op' in order to hold the
;;;; parameters, and make sure '_ is bound to the op.
(defvar _)
(defclass arc-op (arc-action)
((args :accessor args :initform (make-hash-table :test #'equal))
(defmethod render :around ((op arc-op))
(let ((_ op)) (call-next-method)))
;;;; Make sure the 'arg' machinery works with this new slot
;;;; rather than the get/post params, which was the whole point.
(defun arg (op name)
(gethash name (args op) (error "No arg ~A found in ~A" name op)))
(defun input (name &optional (val ""))
(<:input :type "text"
:name (register-callback
(lambda (new-val)
(setf (gethash name (args _)) new-val)))
:val val))
;;;; And this time our example will really work!
;;;; Having though about it for a while, 'req is
;;;; Most likely the current request.
;;;; In CL we just bind a special, *context*.
;;;; Arc lacks special variables AFAIK, so you need
;;;; 'req. Sombody please correct me if i'm wrong.
(defop said
(aform (w/lnk (pr "you said: " (arg _ "foo"))
(pr "click here"))
(input "foo")
(submit))
edit2: explained differences in how _ works in arc and in the CL versions.
yes, it's lisp, so you can do anything with shortcut function/macros, but what matters IMHO is traditional style of doing things.
i'm using UCW for more-or-less usual applications, and find it's not optimal for them either -- it's too hard to figure out how to do stuff, it's bloated, buggy and fragile.
maybe it's "optimal" for some other sort of applications, but i'm yet to see them.
i believe the problem is CLOS. it's tempting to do stuff in "extensible way", but it appears it gets extensible mostly in the way no one needs, and it same time it introduces huge complexity overhead and confusion. arcane method precendence rules instead of function call flow, object slots acting like global variables instead of function parameters. CLOS is evil.
I don't use the 'UCW-AJAX' component library, which i agree is 'bloated, buggy and fragile'. OTOH, The core of UCW (the dispatcher/action mechanism) is very lean, quite stable, and has a comprehensive test suite that ensures what bugs there are don't affect me. It is this core i use in LoL.
But, if you think CLOS is evil and there is something arcane about the precendence rules, you're a lost cause anyway. "object slots acting like global variables instead of function parameters" ... what does that even mean? :P
My last example uses absolutely no CLOS explicitly once the core language is defined, and is identical in almost every way to the arc version. It is through the miracle of CLOS and the MOP that i can so easily change the functionality of something like UCW, in about 5 minutes, to create a PLT-style webserver.
You may find CLOS confusing, but when writing massive lisp applications with constantly changing requirements on teams of 50+ programmers, i've found having an object system that is malliable enough to take the application anywhere, yet that is based on a few simple principles that anybody who has read the MOP understands, is something i would not trade for anything.
Our experiences obviously differ significantly. I never had any major difficulties figuring out UCW and CLOS... have you read Keene, AMOP or any other books that cover OO design in lisp? Maybe they'll help you 'figure out how to do stuff'. AMOP changed the way i view software design... i really recommend it.
Otherwise, have you considered PLT or even (gasp) arc? If you find CLOS confusing, you may be better off going with a simpler approach. If you don't need the component architecture, and you don't like CLOS, why would you use UCW in the first place?
The main different is that Erlang doesn't have syntactic sugar for a single-variable lambda function. However, the total number of tokens is almost the same.
Maybe I'll take some ideas from Arc and use them in ErlyWeb :) Thanks for the illuminating example -- I've never seen this kind of programming style before.
I've been thinking about this challenge in terms of Erlang idioms and worked up a (very) minimal web framework using yaws. With SPEWF < http://code.google.com/p/spewf/ > this might look like:
As I understand it, Arc doesn't yet handle Unicode or anything other than ASCII. Therefore if I say "Espana" (that's n-with-tilde=, or IPA like "ɬɪŋkɪt" (Tlingit), or Chinese like 中國 (China), what's the output page going to say?
Step 5b: Read documentation (what, I'm supposed to read it first?) and see pointer to "blog.arc"
Step 5c: Read header at top of blog.arc which says to run '(load "blog.arc")' followed by '(bsv)'. Okay, I can do '(load "webapp.arc")'
Step 5d: Figure out that '(bsv)' is the function name to start the server, and is specific to that blog code. I need to '(asv)' instead. w00t!
Step 5e: Go to localhost:8080 and find "it's alive". Figured out that I need to go to "localhost:8080/said" to get the web interface.
Step 6: Go to newly started server. Input my home town (contains a diacritic). Oops! The diacritic disappeared. The english spelling of the city's name is not the same as the real name minus the diacritic! Try it out yourself with "Espana" - including the tilde over the n (which you won't see here because this server stripped it away). The english name for spain is not "espana".
(Step 7: Mutter when repeated ^C don't kill the program; did a ^Z; kill %% rather than the (tl) (quit) needed to exit more gracefully.)
I tried various other special characters: the symbol for British pounds (GBP) gets turned into "GBP", the Japanese yen symbol (JPY) gets turned into "JPY". A grep finds this conversion done in "latin1-hack", which is indeed a hack.
Yet upper case sigma (∑) comes back without a problem, as does the traditional Chinese for China (中國). These are encoded through '&' escapes. So why do the Latin-1 hack at all?
Hmm, and the server doesn't specify a charset ... and it doesn't escape embedded text, so if I write "<b>this is not bold</b>" the HTML tags get interpreted.
In summary, the specification says that the final page displays "whatever [was] put in the input field". Yet the given solution does not display "A <GBP>" (that's "A-with-a-circle less-than-sign British-pound-sign greater-than-sign") correctly. The output is "A<GBP>" and the unknown HTML tag is not displayed, so I only see "A".
P.S. This server's session timed out before I finished typing in all of the above so I had to start a new comment and copy&paste from the old. Somewhat annoying.
I take it that none of the arc people are worried that the arc solution to the challenge doesn't work? I can't write the symbol for the British pound or other high Latin-1 characters, and it doesn't escape correctly for display in HTML.
So far I've only seen a couple of people mention the lack of proper Unicode support and the huge XSS hole, and these were people who implemented the complete problem using some other language.
When will there be an arc program which implements the arc challenge?
Yes, I read those. But the point of the challenge is that the last page displays "whatever he put in the input field". I tried out the supposed arc answer to the challenge and it doesn't actually display what I put into the input field.
Try writing "The first conquistador in what is now the US was Juan Ponce de Leon and the last was Don Juan de Onate Salazar." There's an o-with-acute-accent in Leon, and there's an n-with-tilde in Onate).
Try writing "Noroveirusyking a HliX", which is a headline from today's MorgunblaXiX (a newspaper in Iceland).
Try writing "Don't use the <blink> element!"
Or try writing some of the other problems I pointed out earlier. (A parent to this comment.)
* They do not work. *
If the challenge was "... as long as the input is in ASCII and doesn't include the '<' and '>' and '&' characters" then that's different. But that's not the challenge.
At the very least, raise an exception for out-of-range characters. The current code hacks some Latin-1 characters to ASCII, others to "X", and encodes characters >= 256 to &# escape codes. This is wrong.
To which kens added that because the server doesn't set the content-type encoding, if the browser autodetects the ASCII as being utf-7 then there's another possible attack.
And as you can see, this web server doesn't like non-Latin names either. I wonder if the MorgunblaXiX mentioned Paul Erdős' trip from San Jose to Koln. I heard he talked about Mobius strips while eating phở.
Neat-o! Three different modes. Iceland's newspaper got a pair "X"s in the name, some Latin names with diacritics got the diacritics removed, others (the double acute in Paul Erdos) got &escaped.
There are two problems with the code. One is the strange things it does to some characters (stripping diacritics, converting some graphemes to two separate letters by assuming they are ligatures, and so on). Fixing this would not change the code.
The other is that it doesn't escape '<' and '>' correctly so embedded HTML-like text gets improperly interpreted as HTML. One of the advantages of some of the templating systems is the default mode is to escape everything, making it harder to do XSS and other attacks. Fixing that might make the code longer, or not, depending on the solution.
(Just checking if I can make this <b>bold</b>. If so .. hmm.)
I've been able to create one using Ruby and my Web Framework like this:
tt = [
'<form method="POST"><input name="t" /><input type="submit" name="OK"></form>',
'<a href="said?show=1">click here.</a>',
"you said: #{@w.session['t']}"
]
ti = 0
if @w.f(:OK)
@w.session['t'] = @w.f(:t)
ti = 1
elsif @w.q(:show)
@w.session['do_show'] = true
@w.redirect 'said'
elsif @w.session['do_show']
@w.session['do_show'] = nil
ti = 2
end
@w.content = tt[ti]
I'm not sure how other folks will solve it though. It's interesting that my Web Framework has support for both pure Ruby like this, and for a templated approach similar to PHP but using Tenjin:
* http://www.kuwata-lab.com/tenjin/
I wanted to keep it all in one URL and without template for this though. I'm not sure I'd normally program it like that, as I enjoy multiple pages and URLs. Anyway, many ways to skin a cat, and Arc did good.
P.S. In the past my framework had a more "programming" approach which was based on ideas I grasped from Wee which was itself based on Seaside. But since it has been dropped, but your challenge reminds me of it.
Well, yes, that is the beauty of macros. What is holding you back from the Arc example is that it makes you nervous -- how can it work?! I do not see all the moving parts! But then when we are programming heads down do we really want to see all the plumbing? Macrology is all about hiding the boilerplate so we are looking at just what matters. But yes again: make sure your macros (or the ones in the library you choose to adopt) work. :) Once you have satisfied yourself of that, as a developer you are in a much better place. My 2, anywho.
When in ruby using Pannonica (a component based framework):
class ArcChallenge < Pan::Task
def go
msg = call Input.new
call ClickHere.new
call YouSaid.new(msg)
end
end
class Input < Pan::Component
def render_on(html)
html.form {
html.input
html.submit {|val| answer(val)}
}
end
end
class ClickHere < Pan::Component
def render_on(html)
html.link("Click here") { answer }
end
end
class YouSaid < Pan::Component
def initialize(msg)
@msg = msg
end
def render_on(html)
html.lit("you said: #{@msg}")
end
end
It is worth noting the use of the seaside-like continuation based task construct.
Though not as tight as mr. Grahams code it does not lack in clarity IMHO.
var what_the_user_types = new PageVariable();
PageSequence
.define_page_sequence("said")
.with_an.input_field(what_the_user_types).and_a.submit_button
.Then_a_page.with_a.link.saying("click here").to("")
.Then_a_page.with_a.paragraph("you said: ", what_the_user_types);
Some of these code points do nothing, and are just to see how close I can get to typing the spec straight out. Once you remove 'with_a' and 'and_a' code points, you get;
var what_the_user_types = new PageVariable();
PageSequence
.define_page_sequence("said")
.input_field(what_the_user_types).submit_button
.Then_a_page.link.saying("click here").to("")
.Then_a_page.paragraph("you said: ", what_the_user_types);
I'm working on a library for doing continuation-based web-programming with Haskell. Currently, the arc challenge looks like this:
arc = do name <- getInput
link "click here"
display $ "You said: " ++ name
A workflow that asks for two integers (each on a separate page) and multiplies them looks like this:
add = do x <- getInput
y <- getInput
display $ "The product is " ++ show (x * y)
The "getInput"-function is in a typeclass, it generates default instances for basic types like Integers and Strings. This library is based on iTasks (search for clean+iTasks).
This is the only one that I could see someone who has never programmed in their life and has an IQ of less than 100 actually be able to modify successfully. If they wanted to multiply x,y,z with an additional input page, obviously z <- getInput would go after the y <- getInput, and then x * y * z in show's argument. Of course it's not clear why each input gets a new page and what each page might look like or how to customize it or whatever, but it's impossibly readable...
The ABCL implementation isn't bad either...who'd've thought make-page would make a page? I would've thought
get
p/rfd_<asdf>::x[]
set
- insert 10 more lines of random characters -
Because ruby or python or lolperl with one-of-thousands-of-custom-last-minute-user-made-libraries make for easier comprehension of each other's code.(?) If you want to save time programming, stop inventing new languages and libraries every 3 minutes. Everyone start working together on a vast open-source library-of-everything so we can all communicate with our programs the way we can communicate with English by making a big online programmer-dictionary. If every other post on this forum was written in French/Russian/Chinese we would have a difficult time indeed understanding each other. For some reason however it is acceptable to write in 30 different computer languages, each with 30 different libraries meaning I have to learn 900 different things just to understand this new web Hello World program. And so I can take shots at languages, I think some of the perl solutions are beyond terrible. I tried to explain to my girlfriend why I was breaking up with her, but she didn't understand "f<3.$*@$>?><$#####K-->d3" which of course loads 3 continuation-based web applets supporting proxy-qubits that save session info on a multi-router hyperbolic quantum computer. I'm releasing that library shortly, if anyone is interested.
EDIT: made it use sessions to answer the question properly
Here's a solution using the lovely simple web api Sinatra (http://sinatra.rubyforge.org/). How many tokens is this? I didn't use any form helpers, but I'm assuming string literals count as one token.
require 'rubygems'
require 'sinatra'
get '/' do
'<form action="success" method="POST"><input name="message"><input type="submit"></form>'
end
post '/success' do
session[:message] = params[:message]
'<a href="show"> click here </a>'
end
get '/show' do
"You Said: #{session[:message]}"
end
This solution blows--like many others--because it explicitly references a session. Additionally, explicitly referencing a form instead of implicitly providing form values to a procedure is pointless drudgery. Explicit session and state are like having to access all of your Python variables via globals['foo'] or locals['bar'].
The Arc Challenge isn't about Turing completeness: We know you can build a web app using an app server written in Conway's Game of Life, and while that's an interesting curiosity, it's not something that's pushing forward the state of the art. The challenge--for me, at least--is about thinking about how we can make the plumbing of a web application disappear, so we don't need to think about it any more.
;; Should be available at <http://poseur.com:8080/said>.
;;
;; Full source at <http://poseur.com/magic3/magic3.scm>.
(define-handler (said)
(send-xml!
(form (message)
'(((input type text name message))
((input type submit value "Go")))
(send-xml!
(link "click here" (send-xml! `(p you said: ,message)))))))
;; Start thusly: (spawn serve)
In looking at these samples, I think Arc may suffer from the unicycle problem: while the unicycle is the simplest pedaled vehicle, that simplicity makes it the hardest to ride. This greatly limits who can successfully use it. If Arc's designers intend a language with the utility of a bicycle, they must make certain there is enough structure for people to balance, steer, and brake. But at this early stage, Arc looks more like a language for stunt programming.
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use CGI::Session;
my $cgi = new CGI;
my $session = new CGI::Session();
print $session->header();
$session->param('name', $cgi->param('name')) if ($cgi->param("name"));
$session->clear('name') if ($cgi->param("c"));
if ($cgi->param("m") eq 'show') {
print "You said:", $session->param("name"), " ", $cgi->a({href=>'?c=1'},'Start Over');
} elsif ($session->param("name")) {
print $cgi->a({href=>'?m=show'},'See what you said');
} else {
print $cgi->startform, $cgi->textfield('name'), $cgi->submit('Submit'), $cgi->endform;
}
Its simple, its short, and its easy to read, I would guess that anyone from any language should be able to read it. All session and cookie stuff is taken care of automatically, and best of all its easy to expand. Best of all the HTML creation is explicit and the provided examples could be easily replaced with direct HTML or a template system.
obviously this whole thing is just an excercise in showing off the builtin session support of languages...
so i chose to ignore the state management requirement, and submit my entry in BASIC:
Perhaps I'm just dense but ... how does that work just plainly through HTTP? That is, the value not being passed through the URL. How does it get to the server then?
If I knew how to do that first, I might be able to implement it in some other language :)
In other languages, you'd typically store the session in memcached or the database, and then run multiple web frontends that each connect to the shared memcache instance or DB server. Can you serialize closures and store them in an external backend, assuming the existence of memcached and/or database bindings?
(I'm not asking this to prove a point or be a dick...this is a real issue in a lot of deployments. Java/JSF takes the same approach - it stores the complete state of the user interaction in a tree on the server, and then uses either a cookie or URL parameter to retrieve that state. A coworker and I spent a couple weeks digging into the JSF internals to get it to operate statelessly; the base JSF framework worked fine with a configuration change, but the AJAX framework built on top of it choked miserably.)
What did you do to get the base JSF to work on multiple servers? I am having that issue now - whenever a server switch is done, the context set up by JSF is lost and a blank page shows. Results 2 thru n on the same server are fine, result 1 being the initial page (JSP) request. Thanks.
It'll serialize the UIComponent tree and store it in a hidden input field with every interaction, then restore the view from that field. Naturally, this doesn't work if you're using GET for forms. (There's an undocumented feature of JSF where you can change the form method using JavaScript and make it submit information via GET. It tends to break though - you can easily overflow query strings, and I recall some problems when binding components to bean properties.)
You have some page with an edit box and a submit button. When you submit, the data in the box transfers to the server, which displays a new page, which relies on session data to print something to your screen.
Your challenge is to create an application where the behavior of the second page can't be manipulated by by altering the URL.
The reason I don't quite understand the question is that I don't know how the contents of the text box gets to the server in the first place. Certainly once the text is on the server, if the server relies on closures for the second page, then it cannot be manipulated. Say it's http://arclanguage.com/second/cid=3. You can't change that pages because by the time you hit that URL, the text is already in the server and it operates with closures.
OK, I get that part. The part I don't understand is... the data has to get to the server sometime. If you're making a post to http://arclanguage.com/first then you can _effectively_ change the second page by altering the data then. So perhaps it's not in URL; that is, not like http://arclanguage.com/first?text=the%20entry. It's POSTDATA or whatnot. But you can still manipulate post data.
The data is not used on the second page, but on the third.
Submit on the first page sends the data using http post. The second page just displays a link "click here", and it's when following that link the user is unable to alter the data.
Normally, you (or the language/framework you use) will set a cookie with a unique session id, then store the value in memory on the web server or in a database referencing this session id.
Then when the new request comes, the user agent (browser) will send the session id back to the server. You can then use it to look up the original value.
I agree with kc5tja and will expand on his/her comments.
I question the value of simply making a program shorter. A shorter program will have fewer bugs than a longer one but even that is not the point. The point is that any program worth keeping will have to be maintained.
The critical question is "will the person who has to maintain the code understand the code as written and thus be able to maintain it correctly?" I suggest that both very terse and very verbose languages fail to communicate the what, why, and how by their code alone. They require massive external documentation to support understanding. THAT alone defeats the goal of simply making the code shorter.
I suggest the correct goal is to create a language that MINIMIZES the total of code, documentation, training, and learning curve to maintain it correctly. Unfortunately, that goal is orders of magnitude more difficult to achieve than merely short code.
In my 40+ years of system development, there is one language that achieves the goal of shortness without comprise: APL. Its close to possible to simulate today's weather starting from the big bang using no more than two or three lines of code using APL. However, once you get it working, neither you nor anyone else can comprehend the how, what, and why of the code. APL is the ultimate language for the disposable program: use it once and delete it. Is that really your goal for ARC? If it is, then ignore my comment.
One last question. If your goal is to optimize the production of disposable programs, have you considered the well known effect that such programs become part of "the product" as promised by the marketing department and/or some out of control salesman? Then, in a few months, they will demand that hundreds of new and incompatible features be added to make the product "better"? Will the terseness of your language save you then or will you endlessly be starting over from scratch? Your goal contains not only the seed of your defeat but also its leaf, branch, trunk, and roots.
There is a future in which the past must be supported. Have pity on those who follow you. Do your job right and they will praise you. Continue on this path and your name will be brightly lighted with the flames of oblivion.
> However, once you get it working, neither you nor anyone else can comprehend the how, what, and why of the code.
I think I agree, except that your example is bad - complex weather would emerge from /simple/ cell interactions, and the program itself would be short and simple. But that's a similar idea to what I'd fear from a maximally compressed language - what if someone could write terse code where the meaning emerges from interactions in a way that only a genius could follow? It could be impenetrable and unmaintainable to non-geniuses. But I guess an ordinary language could have such programs, as well.
There's another weakness (I think) in pg's criteria for Arc, though he probably just failed to mention it, or just as likely I failed to notice - it would be possible to make Arc programs shorter by multiplying custom operations in Arc for all occasions. His criteria should be, minimize /N x the length of Arc plus the length of each program in some representative set/.
> But I guess an ordinary language could have such programs, as well.
Yes. In fact a very high percentage of all software written falls into that category. The challenge is to write clear maintainable code in any language. The language can help or hinder the process but it cannot solve the problem (aka there is no silver bullet). Language terseness compounds the problem mostly by extending the learning curve beyond economic practicality.
PS: if you want a really tiny language, use FORTH. Its trivial to write totally incomprehensible programs in it. Its compiler, os, and executing environment can live in less than half of a 1K 8 bit computer. Multiple programs can live in the rest. Its a brilliant concept that totally solves the wrong problem.
Nice work, pg. One should also note that languages not built around making web apps will suffer due to the combinatorial explosion of web app libraries implementing what should be in the language layer. For example, Python.
This example requires some input to be stored for some time within a common workflow of a user (perhaps within 15 minutes, within a session). How does this concise example within Arc behave if there are requirements about the input (not just the reappearing unicode argument but perhaps some sort of identification key that is only numeric or stored within a database) and about the state beyond server reboots (adding an item to a cart to be checked out and paid for in the third step, which might have days between them).
Rails shines in making common crud actions coded easily when handling models that can be expressed in a model stored in one row of a database. This arc challenge thus far only shows me that arc shines in a wizard like behavior that is carrying an input across 3 pages (with the layout of those three pages being very concise or not with a standard beyond web 0.1 layout)
The power of a language is not expressed in one application (web based interfaces without an obvious use in this example case). It is expressed, I feel, in the easy a devoloper can solve the problem which allows another developer to evolve the current code to solve evolving requirements. This places a burden on the base language and the libraries expressed with this language.
Arc is the best language to express this example in. How does Arc rise to the challenge of expressing "99 bottles of beer on the wall" within one, concise character? (http://www.cliff.biffle.org/esoterica/hq9plus.html or 9)
The real challenge for a new (if that) programming language is to provide case studies that evolved over time with consistent maintainability and quality. (Software quality is another debate involving the power of users or clients to recognize quality of software or why firefox is not yet the most standard browser).
This challenge isn't it.
edit: I apologize for the abundance of parentheses within my comment.
Many of the translations so far use some kind of session abstraction. That doesn't accomplish quite the same thing as the Arc example, and in particular will probably break across multiple windows.
Maybe the challenge should also require that you be able to:
* submit the form in one window, then open a new window and go through the complete sequence.
* perform the steps twice, if you go back to the original URL.
I do agree with previous poster christantillo - just the number of nodes a language needs may not be the perfect test. But I also find it reasonable to go with what Paul Graham points out in his own comments - it is a good measure.
Having said that I must say that the arc solution is quite readable, while some other solutions are harder to understand. This is a very subjective statement (and probably displays more of my incompetence in some languages than in my competence in others), but if sufficiently many people are of the same opinion then it may be a useful observation.
I do have some comment on the actual task, though: Doesn't the challenge play to a particular strength of arc? Also isn't there some dependence on the available libraries? If some language doesn't have a powerful library in which the benchmark takes place it will naturally look bad.
I could come up with a task that will be ugly in arc, but plays to a particular strength of another library in a different language. Now, I could replicate that library first in arc (ignoring how long and complicated that may be) and only then count the length of the arc solution which utilizes the library.
But then I am not sure if this would still be a fair comparison.
[Edit: After having tried to write a word in my foreign language (German) I could e.g. suggest an extended challenge which reads the answer in any language. (Actually, some solutions in this challenge will work "correctly" in this sense.) Then the arc solution would indeed require me to write a complicated library first and it would no longer be competitive length-wise.]
This challenge has made me create three different solutions all present in the comments, but I'll show where they are within my custom Web Framework:
dewd@rubynho:~/code/libraries/web_rules/examples/arcchallenge$ ls
jsp_said.rtj php_said.rtj said.rb
The cool thing is that they work simultaneously, just like multiple "php pages" would in a PHP setup. The future should look like this in my opinion. :-)
The program listens to 'http://localhost:8080/said'. First it shows a page with an input field and a submit button. Clicking the submit button shows another page with a link saying "click here". Clicking the link shows the last page with the text "You said: " along with the text typed in on the first page.
One might say that this is the definition, but in fact this is the solution. The verbs 'listen', 'show', 'click' and the nouns 'input field', 'submit button', 'link' or the reference 'the text typed in on the first page' are all context sensitive. A human can interpret this program by using some paper and a pen while another human could be a user. Unfortunately a computer cannot yet do this.
So how do you write it in lisp? Well, the best advice here is if you still have a couple then use your father's parenthesis. Otherwise you will have to make up some new ones such as.
Defining the terms entry, show, input-field, submit-button, link and text in a 'framework' of your choice is left to the reader as an exercise. Each one of those are generic concepts.
Just a general comment here (hope it's appropriate). I think it's terrific to make programs as concise as possible, but I don't think that "The most meaningful test of the length of a program is not lines or characters but the size of the codetree".
I think the length is better measured in time units, that is, by a) how long it takes to write it the first time and b) how long it takes to come back and make a meaningful modification to it.
I mean something like asp.net would generate a whole bunch of code to solve this problem, but it wouldn't really take that long to produce it.
Somewhere in the effort to produce the simplest language might there be a place for code generation tools, IDEs and, well, any other tool that speeds up the problem at hand? Funny that computer programmers are often the last people to ever want to actually use a computer program (think vi and notepad enthusiasts).
Will the beauty of Arc be confined to software written as ascii characters?
Anyway, that's just a comment; I really do love this stuff and think the work on Arc is wonderful.
Chris Tantillo
PS Hey - that's funny. I had to create an account after I posted this and I thought it would loose my text. But it remembered it - isn't that what the challenge is trying to do? You know, enter text (page 1), click a link (page 2), see text again (page 3). Pretty slick there :)
Regarding your PS: Yes, I agree. That's working extremely well. Isn't this forum designed in arc?
Edit: I tried to write something in my native language (German). But it doesn't work. This really seems to be a major drawback of arc. So far, you can only use it for English boards.
i stopped befoere finishing this; the middle piece could
be called SILENCE. Anyway there are a variety of choices
for the session model -- to keep "it should not be possible to change the behavior of the third page by editing the url in the second." we either need hard-to-guess url keys or
cookies, which are essentially url keys that simple mortals
are not aware of.
#!/usr/bin/perl
use HTTP::Server::Singlethreaded
function => { '/' =>
sub {
$counter++; # we need a session, right?
<<LIGHTNING
Content-type: text/html
I paste Lukas's Seaside solution below because some people might have missed the link above, so it might not get the attention it deserves. Although it's a bit late I guess. Click the link above to see it in action.
What a great way to get a lot of smart people to propose new ideas! What I would like to know is Paul's criteria for parsing these ideas. For example:
1. What makes this a good (bad) idea?
2. How to distinguish between language and framework (its been mentioned before).
3. Should they/can they be included/morphed into Arc, or is Arc kind of now too "set" (eg what sort of ideas can be included).
4. Or can cool ideas be added as libraries or something?
5. Some idea of the thought process that Paul goes through when reading submissions - that would be interesting to hear.For example I imagine overcoming the natural NIH bias must be challenging.
1. You can only accomplish what is possible.
2. Just because you can do it is no reason to do it.
3. What you do and how you do it depends upon your goal.
4. Context modifies nearly everything.
5. What is good or bad depends upon the ultimate value.
Knowing the relevant context and deciding the ultimate value is the challenge. The rest is easy by comparison. Both context and values are hardly ever discussed when it comes to programming projects. The discussions mostly reduce to "look at this cool thing I did - want to do - plan to do." The past is considered to be irrelevant and the future does not exist. Its only the eternal NOW and the excitement of an out of context fantasy. In such fertile soil are the seeds of almost universal failure with vast cost over runs, endless schedule extensions, and delivery of less than promised projects.
What about posting a bid in rentacoder.com or similar:
Project:
"First: generate a page with input field and submit button. If the user clicks on submit, he gets a second page with a link saying "click here." If he clicks on that, he gets a third page saying "you said: ..." where ... was whatever he put in the input field. This has to happen without the value being passed in the url; it should not be possible to change the behavior of the third page by editing the url in the second"
I think that's another programming language evolution to consider.
Using a functional web language called Links, currently under development at Edinburgh university... http://groups.inf.ed.ac.uk/links/
page <#>{input => fun (x) {page <a l:href="{page <#>{stringToXml(x)}</#>}">Click Here</a>}}</#>
All the XHTML is totally customisable if I want it to be, just keeping the code as short as possible here. Uses no libraries or anything, that's just the basic language.
(define-macro (s id)
`(selfish ,(x->string id)
(form/cont/ (entry-lambda (:keyword ,id)
(div/ (a/cont/ (cut p/ "you say:" ,id) "click here")))
(readln/ ,id)
(submit/))))
(define-main-page (said5)
(node-set/ (s say1) (s say2) (s say3) (s say4) (s say5)))
Of course, these 5 said parts work independently.
When text string are filled and click other link or post other submit,
these filled text are kept automatically.
Characteristically as continuation based system,
browser's back button attack and tab clone attack make no problem.
// using my personal library of functions for PHP...
// I will test this when I get back to my computer at home, but it should work... basically the two functions should be self explanatory.. I should mention though that these functions are not built just to make it seem small, I use these quite a bit... and of course I have much more complicated ones for more complicated tasks
Looks awesome. Now for me the issue is never the space taken up by code, or its comprehensibility, but how long it takes to produce the code (i.e. the thinking process). I'm a fast typer, so whether its five or ten lines doesn't matter to me. I'm also a fast thinker but ultimately, the ideal language is the one one can "think in" the fastest.
All that said, I'll give it a try, hopefully sooner rather than later.
Since they challenge has been tackled in a technial fashion already, I thought I would challenge it in a philosophical fashion, since you are looking for the Platonic Lisp after all.
Classic ASP:
if Len(Session("x21")) then
Response.Write "You said " & Session("x21")
elseif Len(Request("x21")) then
Session("x21") = Request("x21")
Response.Write ""arc.asp""
else
Response.Write "<form><input name=""x21"" /><input type=""submit"" /></form>"
end if
LispNYC ( a lisp user group in New York ) has, in the last year or so, seen the debut of at least 3 lisps: otter by perry metzger, clojure by rich hickey, and nyclisp by me. Nyclisp is by far the least mature, but I'm trying to clean it up a bit for a release.
you could do this with netcat and a couple
sh scripts, if you really wanted to
it's an interesting golf hole; as posed however
the bounds of how much framework can be implied is
not specified though. That would need to be part of the
challenge, for a better golf hole.