Arc Forumnew | comments | leaders | submitlogin
How would this Arc program be translated into other languages?
59 points by pg 2515 days ago | 206 comments
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:

  (defop said req
    (aform [w/link (pr "you said: " (arg _ "foo"))
             (pr "click here")]
      (input "foo")
      (submit)))
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.

(Gag submissions will be deleted...)



37 points by elibarzilay 2515 days ago | link

With the PLT web server:

  (define cc (make-parameter #f))

  (define (input name) `(input ((type "text") (name ,(format "~a" name)))))
  (define (submit) `(input ((type "submit"))))
  (define (form . body) `(form ((action ,(cc))) ,@body))
  (define (a ref . body) `(a ((href ,ref)) ,@body))
  (define-syntax page
    (syntax-rules ()
      [(page x ...) (send/suspend (lambda (k) (cc k) `(html (body ,x ...))))]))
  (define (get name req)
    (extract-binding/single name (request-bindings req)))

  (define (start initial-request)
    (define foo (get 'foo (page (form (input 'foo) (submit)))))
    (page (a (cc) "click here"))
    (page "you said: " foo))

-----

38 points by GreyLensman 2514 days ago | link

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."

-----

19 points by matthiasf 2514 days ago | link

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.

-- Matthias

-----

2 points by NickSmith 2513 days ago | link

Thanks Grey. I am in the process of teaching myself Scheme (via SIPS) and find your comment quite enlightening.

-----

2 points by croach 2511 days ago | link

Hi Nick,

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.

-----

1 point by NickSmith 2511 days ago | link

I perhaps need to first teach myself to type. I meant SICP.

-----

1 point by croach 2510 days ago | link

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.

-----

4 points by NickSmith 2510 days ago | link

Sorry to disappoint, but here's a book that I do recommend: http://www.amazon.com/Little-Schemer-Daniel-P-Friedman/dp/02...

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

-----

2 points by vincenz 2511 days ago | link

Out of curiousity,

How do you run this?

-----

6 points by willpost 2511 days ago | link

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.

(module test mzscheme (require (lib "servlet.ss" "web-server")) (provide (all-defined)) (define interface-version 'v1) (define timeout +inf.0)

  (define cc (make-parameter #f))

  (define (input name) `(input ((type "text") (name ,(format "~a" name)))))
  (define (submit) `(input ((type "submit"))))
  (define (form . body) `(form ((action ,(cc))) ,@body))
  (define (a ref . body) `(a ((href ,ref)) ,@body))
  (define-syntax page
    (syntax-rules ()
      [(page x ...) (send/suspend (lambda (k) (cc k) `(html (body ,x ...))))]))
  (define (get name req)
    (extract-binding/single name (request-bindings req)))

  (define (start initial-request)
    (define foo (get 'foo (page (form (input 'foo) (submit)))))
    (page (a (cc) "click here"))
    (page "you said: " foo))
  
)

2 - Start the PLT web server sudo /usr/plt/bin/plt-web-server

3 - Open a web browser and navigate to http://localhost/servlets/examples/test.ss

An easy way to check for errors is to open it in DrScheme, click "Run" and they would be highlighted in red.

-----

23 points by massung 2512 days ago | link

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."

Jeff M.

-----

1 point by micky 2374 days ago | link

"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

-----

17 points by nostrademons 2514 days ago | link

Javascript, all done client-side, using JQuery:

  $('body').append('<input id = "myInput" /><input type = "submit" />')
    .find('input[@type=submit]').click(function() {
       val = $('#myInput').val();
       $('body').html('<a href = '#'>click here</a>').find('a').click(function() {
          $('body').html('You said: ' + val);
       });
    });

-----

7 points by tlrobinson 2513 days ago | link

Without JQuery...

<input id="in" type="text" /><input type="submit" onclick="window.result='You said: '+document.getElementById('in').value; document.body.innerHTML='<a href=\'#\' onclick=\'document.body.innerHTML=window.result;\'>click here</a>'" value="Submit"/>

(it's a big hack, I know)

-----

3 points by bdeterling 2512 days ago | link

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'))"/>

-----

-3 points by name 2209 days ago | link

vffgf

-----

1 point by murkt 2514 days ago | link

Nice! Compare to Ruby ones :)

-----

6 points by EliAndrewC 2515 days ago | link

If I were using Python and CherryPy then I'd say something like:

    class Root:
        def form(self):
          return render("form.html")
        
        def link(self, foo):
          cherrypy.session["foo"] = foo
          return render("link.html")
        
        def message(self):
          data = {"foo": cherrypy.session["foo"]}
          return render("message.html", data)
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.

-----

18 points by killerstorm 2515 days ago | link

Common Lisp, ABCL-web(http://abcl-web.sourceforge.net/web-engine.html):

    (defun said ()
      (make-page "a" 
       (action-form (((:input :name "foo"))
                      (form-submit "s"))
        (make-page "b"
          (action-link "click here"
             (make-page "c"
                (html (:princ "you said: " (getf form-data :foo))))))))
pretty much same, modulo shortcut functions, but i'd say it's easier to understand code in ABCL-web because code flow matches control flow.

-----

2 points by ryantmulligan 2513 days ago | link

I like the control flow better than the arc example.

-----

2 points by pg 2514 days ago | link

Defun defines something that works in urls?

-----

2 points by killerstorm 2513 days ago | link

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.

-----

2 points by pg 2513 days ago | link

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.

-----

1 point by killerstorm 2512 days ago | link

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:

    (setf (gethash "said" *views*)
          (view-definition (action-form ...
because it was made as an experiment to submit/publish functions via web, and so there's no macro like defpage to do this.

i'm actually planning to finalize this stuff and release it as live demo -- so people can try programming it online -- soon.

-----

4 points by randallsquared 2515 days ago | link

The way I do PHP, there'd be two templates, a.html and c.html (for example), and a separate page b.html. Then, a.php would have

    <?php
    require_once('liball.php');
    
    if ($foo = $_POST['foo']) {
      $_SESSION['foo'] = $foo;
      localredirect('b.html');
    }
    sendpage('a');
    ?>
The template a.html has the form, and b.html just has the link on it.

Assuming page c needs to be protected against people just coming to it:

    <?php
    require_once('liball.php');
    if (!($foo = $_SESSION['foo'])) {
      localredirect('a.php');
    }
    sset('foo', $foo);
      
    
    sendpage('c');
    ?>
and the c.html template has in it

    you said: {$foo}
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.

-----

8 points by randallsquared 2515 days ago | link

Perhaps a more traditional PHP version is:

    <?php
    // unvarying HTML elided above
    if ($foo = $_POST['foo']) {
      $_SESSION['foo'] = $foo;
      print '<a href="">click here</a>';
    } else if ($foo = $_SESSION['foo']) {
      print 'you said: ' . $foo;
    } else {
      print "<form method='post'><input type='text' name='foo'><input type='submit'></form>";
    }
    ?>

-----

1 point by bonzinip 2509 days ago | link

More complicated, but scales better to more complex problems and it is back-button friendly.

    <?php
      $step = isset ($_POST['step']) ? $_POST['step'] : 0;
      if ($step == 0)
        $_POST = array();

      $_POST['step'] = ($step + 1) % 3;
      echo '<form method="post">';
      foreach ($_POST as $k => $v)
        echo '<input type="hidden" name="' . htmlspecialchars ($k) . '"
            value="' . htmlspecialchars (stripslashes ($v)) . '" />';
    
      echo '<p>';
      switch ($step)
        {
        case 0:
          echo '<input type="text" name="simon-sez">';
          break;
        case 1:
          echo 'Simon sez...';
          break;
        case 2:
          echo htmlspecialchars (stripslashes ($_POST['simon-sez']));
          break;
        }
      echo '</p>';
      echo '<input type="submit" value="Go on" />';
      echo '</form>';
    ?>

-----

1 point by rekall 2392 days ago | link

thank you for reaffirming my faith in php.

-----

8 points by kc5tja 2513 days ago | link

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.

-----

12 points by sketerpot 2513 days ago | link

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.

-----

5 points by lojic 2515 days ago | link

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

-----

6 points by lojic 2515 days ago | link

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.

-----

3 points by lojic 2515 days ago | link

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.

-----

4 points by akkartik 2514 days ago | link

> I haven't been able to get to the point of controlling 100% of the presentation via CSS alone..

I think that's the real reason PG uses tables.

-----

2 points by lojic 2513 days ago | link

BTW, this is working code. I just had to add the following functions to app/controllers/application.rb:

aform, input, pr, submit, wlink

all one liners except for aform which is 3

-----

9 points by pc 2513 days ago | link

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.

http://www.google.com/search?hl=en&q=%22don%27t+use+the+..., http://www.google.com/search?hl=en&q=%22don%27t+click+th...

-----

6 points by kens 2512 days ago | link

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?

-----

4 points by pc 2511 days ago | link

"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.

-----

3 points by olavk 2510 days ago | link

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.

-----

1 point by pc 2491 days ago | link

"Continuations are only an options for handling URL-based state"

This isn't true.

-----

6 points by emmett 2512 days ago | link

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.

-----

1 point by JoshKingBoston 2511 days ago | link

Post/Redirect/Get (http://en.wikipedia.org/wiki/Post/Redirect/Get) is the relevant design pattern to handle this situation.

-----

11 points by Kashia 2514 days ago | link

  #!/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

-----

2 points by troelskn 2512 days ago | link

> 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.

-----

1 point by ix 2512 days ago | link

element http://repo.or.cz/w/element.git :

  case r.qs
  when '': isindex
  when /^@/: r.qs.tail
  else a'follow',:href =>'?@'+r.qs
  end

-----

10 points by emmett 2515 days ago | link

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...

-----

9 points by brett 2515 days ago | link

you don't really need callcc as much as just storing proc closures

here's a ruby version a bit closer to the original. there's a bunch of support and then process roughly corresponds to said above

  #!/usr/bin/env ruby
  require 'rubygems'
  require 'mongrel'

  class FooHandler < Mongrel::HttpHandler
  
    def initialize
      @fnids = {}
      @c = 0
    end
  
    def new_fnid(proc)
      @c += 1
      @fnids[@c] = proc
      @c
    end
  
    def query_params(request)
      request.class.query_parse(request.params['QUERY_STRING'])
    end
  
    def pr(response, html)    
      response.start do |head,out|
        head["Content-Type"] = "text/html"
        out << html
      end
    end
  
    def w_link(response, link_text, &block)
      c = new_fnid(block)
      pr(response, "<a href='?fnid=#{c}'>#{link_text}</a>")
    end
  
    def aform(response, form_html, &block)
      c = new_fnid(block)
      pr(response, "<form><input type='hidden' name='fnid' value='#{c}'>#{form_html}</form>")
    end
  
    def process(request, response)
      if (fnid = query_params(request)['fnid'])
        @fnids[fnid.to_i].call(request, response)
      else
        aform(response, "<input name='foo'><input type='submit'>") do |req1, resp1|
          w_link(resp1, "click here") do |req2, resp2|
            pr(resp2, "you said: " + query_params(req1)['foo'])
          end
        end
      end
    end
  end

  Mongrel::Configurator.new :port => 8080 do
    listener {uri "/", :handler => FooHandler.new}
    trap("INT") {stop}
    run
  end.join

-----

3 points by s3graham 2515 days ago | link

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).

-----

3 points by lojic 2515 days ago | link

You can use request.post? instead of request.method == :post if you like.

-----

2 points by saharrison 2515 days ago | link

Ruby's callcc is not a true continuation -- you can't store the state of the continuation.

-----

1 point by pc 2512 days ago | link

Huh?

-----

1 point by simen 2508 days ago | link

Ruby's continuations can't be serialized. That does not make them any less true continuations, though.

-----

12 points by awwaiid 2515 days ago | link

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");
  }

-----

4 points by ap 2513 days ago | link

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).

-----

2 points by hobbified 2508 days ago | link

  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 %]".

-----

7 points by s3graham 2515 days ago | link

Perl, I missed you! That one's pretty.

-----

6 points by gjohnson 2488 days ago | link

Just for kicks, here it is solely in HTML and CSS. Ah...gotta love those target selectors.

    <!DOCTYPE HTML>
    <html>
        <head>
            <title></title>
            <style type="text/css">
            #page2{display:none}
            #page2:target{height:100%;width:100%;position:absolute;top:0;left:0;background-color:#ffffff;z-index:2;display:block}
            #page13:target input[type="submit"]{display:none}
            #page13 p{display:none}
            #page13:target p{display:inline}
            #page13:target input{display:inline-block;border:0px}
            </style>
        </head>
        <body>
            <div id="page13" class="page">
                <form action="" method="post">
                <p>You said:</p>
                <input type="text">
                <div style="position:relative">
                    <input type="submit">
                    <a href="#page2" style="position:absolute;top:0;left:0;height:100%;width:100%"></a>
                </div>
                </form>
            </div>
            <div id="page2" class="page">
                <a href="#page13">click here</a>
            </div>
        </body>
    </html>

-----

2 points by gjohnson 2488 days ago | link

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

-----

2 points by sacado 2488 days ago | link

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"

-----

2 points by gjohnson 2487 days ago | link

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

  ~Keep on hackin' in the Free world.

-----

2 points by eds 2487 days ago | link

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.

-----

4 points by gjohnson 2487 days ago | link

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?

Just my 2 cents here. Open to feedback as always.

-----

4 points by sacado 2487 days ago | link

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.

-----

3 points by gjohnson 2487 days ago | link

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.

-----

11 points by s3graham 2515 days ago | link

Python, plus my in-house web framework:

    from pyricks import *
    class Said(PyricksApp):
        def initSession(self, root, data):
            def onsub(foo):
                def yousaid():
                    Yield("you said: " + foo)
                Yield(Link("click here", yousaid))
            Yield(Form(Input("foo"), onsubmit=onsub))
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?

-----

1 point by ingspree 2513 days ago | link

That would be interesting to see your framework opensourced. :)))

-----

16 points by pc 2515 days ago | link

  | foo |
  foo
    ifNil: [html form: [html textInput callback: [:t | foo := t]]]
    ifNotNil: [html anchor callback: [self inform: 'You said ', foo]; with: 'click here']

-----

4 points by ben 2513 days ago | link

In case anybody is wondering, this is smalltalk and Seaside.

-----

1 point by cwp 2513 days ago | link

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?

-----

2 points by pc 2513 days ago | link

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.)

-----

6 points by hns 2512 days ago | link

Server side Javascript with Helma and http://gobi.helma.at/Documentation/Developers/MarkupLib/:

  function said_action() {
    Html.form({
        callback: function() {
            var foo = req.data.foo;
            Html.link({
              callback: function() { res.write("you said: " + foo); }
            }, "click here")
        }, method: "post"},
      Html.Input({name: "foo"}), 
      Html.Submit()
    );
  }
Or, a bit more readable:

  function said_action() {
    var foo;
    var step1 = function() {
        foo = req.data.foo;
        Html.link({ callback: step2 }, "click here");
    };
    var step2 = function() {
       res.write("you said: " + foo);
    };
    Html.form({callback: step1, method: "post"},
      Html.Input({name: "foo"}), 
      Html.Submit()
    );
  }
This one actually uses callbacks stored on the server, like the Arc example.

-----

1 point by chl 2512 days ago | link

Very nice! I had totally forgotten about MarkupLib's callback superpowers.

-----

7 points by dood 2515 days ago | link

In Python, using the Pylons framework fairly normally

    class HelloController(BaseController):
        def hello_pylons(self):
            if session.get('foo'):
                c.content = 'you said ' + session['foo']                    
            elif request.POST.get('foo'):
                session['foo'] = request.POST['foo']
                session.save()
                c.content = h.link_to('click here')            
            else:
                c.content = h.form('') + h.text_field('foo') + h.submit() + h.end_form()
            return render('template.mako')

-----

6 points by timb 2515 days ago | link

  <html><body><script type="text/javascript">
  (function (){
    d = document; b = d.body
    function nu(n,t){
      var e = d.createElement(n)
      if(t.h) e.innerHTML = t.h
      for(var k in t) e[k] = t[k]
      return e
    }
    function ad(o){ b.innerHTML = ''; for(var i=0, o; o=arguments[i]; i++) b.appendChild(o) }
    ad(
      nu('input', {id: 'foo'}),
      nu('button', {h: 'go', onclick: function(){
        f = d.getElementById('foo').value
        ad(
          nu('a', {h: 'click here', onclick: function(){
            ad( nu('p', {h: 'you said ' + f}) )
        }}))
    }}))
  })()
  </script></body></html>

-----

8 points by jnl 2511 days ago | link

Ruby can be even more concise:

  def said
    aform(input("foo"), submit) {
      w_link("click here") {
        "you said: #{arg :foo}"}}
  end
Support code here: http://arc-challenge.heroku.com/

-----

1 point by jnl 2510 days ago | link

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:

  def said
    aform(input("foo"), submit) {
      aform(input("foo"), submit) {
        msg = "you said: #{arg :foo}"
        w_link("click here") {
          "you said: #{arg :foo}<br />" + w_link("click here") {
          msg}}}}
  end

-----

6 points by mikerundle 2513 days ago | link

Okay well here's something I have to ask.

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?

-----

2 points by kens 2512 days ago | link

Against my better judgement, here's a JSP solution:

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <title>Contrived example</title></head><body><div>
  <% if (null == session.getAttribute("page")) {
        session.setAttribute("page", "1"); %>
        <form action="said.jsp" method="post">
        <div><label>Enter something <input type="text" name="foo"/>
        <input type="submit"/></label></div></form>
  <% } else if ("1".equals(session.getAttribute("page"))) {
        session.setAttribute("page", "2");
        session.setAttribute("value", request.getParameter("foo")); %>
        <a href="said.jsp">click here</a>
  <% } else if ("2".equals(session.getAttribute("page"))) {
        session.setAttribute("page", null); %>
        you said: <%= org.apache.commons.lang.StringEscapeUtils.escapeHtml((String)session.getAttribute("value")) %>
  <% } %>
  </div></body></html>
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 :-)

-----

1 point by namaste 2512 days ago | link

Here's this same thing translated to Ruby + custom Web Framework + Tenjin (which does the escaping with the ${} command. #{} and it does not escape.)

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <title>Contrived example</title></head><body><div>
  <?rb if not @w.session['page']
        @w.session['page'] = '1' ?>
        <form action="jsp_said" method="post">
        <div><label>Enter something <input type="text" name="foo"/>
        <input type="submit"/></label></div></form>
  <?rb elsif @w.session['page'] == '1'
        @w.session['page'] = '2'
        @w.session['value'] = @w.f('foo') ?>
        <a href="jsp_said">click here</a>
  <?rb elsif @w.session['page'] == '2'
        @w.session["page"] = nil ?>
        you said: ${@w.session['value']}
  <?rb end ?>
  </div></body></html>

-----

4 points by serhei 2513 days ago | link

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.

-----

13 points by murk 2514 days ago | link

Haskell:

    serveAs "said" $ hasIndex $ \x -> "click me" `linksTo` (text ("You said " ++ x))

-----

12 points by vegai 2514 days ago | link

It looks like it is using a hypothetical web library.

Certainly would be interesting to see it implemented.

-----

7 points by hagbart 2514 days ago | link

could you explain that code a bit? Which framework are you using?

-----

2 points by purejadekid 2511 days ago | link

This time, some real code in Haskell:

------

said = standardQuery "What's your name?" (p_T (do { text_S "Hi there! What's your name?" ; activate greeting textInputField empty }))

greeting :: String -> CGI ()

greeting name = standardQuery "Hello" (do { text_S "Hello " ; text name ; text_S ". This is my first interactive CGI program!" })

------

where said <==> helo5, code at http://www.informatik.uni-freiburg.de/~thiemann/WASH/Tutoria...

Try it, live (chose Hellow World Personalized):

http://nakalele.informatik.uni-freiburg.de/cgi/WASH/Tutorial...

-----

7 points by ingspree 2514 days ago | link

But... Where is the form? I just don't understand.

-----

4 points by Gotttzsche 2513 days ago | link

It looks like the \x does the form. O_o That would be weird.

The last part looks pretty lispy.

Does this mean Haskell wins against Arc? :p

-----

3 points by murk 2513 days ago | link

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` :-)

-----

1 point by ingspree 2513 days ago | link

Hm,, interesting idea, though hasIndex can be written in Arc to simplify their code.

But I like Haskell. :-) Interesting, how long will be explicit declaration of form.

BTW, this is your imagination or you are using something real and this can be used just now for web programming?

-----

5 points by murkt 2514 days ago | link

Brilliance!

-----

1 point by lojic 2513 days ago | link

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.

3) Produce valid html that passes:

http://validator.w3.org/check

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.

-----

4 points by drewc 2515 days ago | link

Here's what this looks like in Lisp on Lines :

    (defcomponent page (info-message)
      ()
      (:render ((self page))
        (if (message self)
           (<:as-html "you said: " (message self))
           (<lol:form :action (call 'info-message :message "Click Ok")
             (<lol:input :accessor (message self))
             (<:submit)))))
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...

-----

5 points by drewc 2515 days ago | link

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:

    (defcomponent page (info-message)
      ()
      (:render ((self page))
       (<lol:form 
           :action 
             (progn (call 'message :message "click here")
                (call 'message :message (format nil "you said:~A"  
                                                         (message self))))
              (<lol:input :accessor (message self))
              (<:submit)))))

-----

13 points by Chac0 2515 days ago | link

I'm beginning to see why Paul started Arc.

-----

4 points by drewc 2515 days ago | link

Why's that exactly? Did you find Common Lisp too readable? ;)

-----

6 points by killerstorm 2515 days ago | link

i find this LoL code object-obsessed and unreasonable verbose because of this: how many time you repeat message/message/message?

i never used LoL, but coding with UCW is PITA for me.

-----

9 points by drewc 2515 days ago | link

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.

-----

4 points by killerstorm 2514 days ago | link

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.

-----

5 points by drewc 2514 days ago | link

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?

-----

4 points by yariv 2513 days ago | link

In Erlang, using a hypothetical Arc-like web framework:

said(_Req) -> form(fun(Data) -> link(["you said: ", arg(Data, "foo")], "click here") end, input("foo"), submit()).

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.

-----

1 point by partdavid 2512 days ago | link

Thanks, I was scanning this for Erlang and took a look at ErlyWeb to see if it could help me approach it, actually.

-----

1 point by yariv 2510 days ago | link

Typo: different -> difference.

Also, the last two parameters should be in a list, i.e.

said(_Req) -> form(fun(Data) -> link(["you said: ", arg(Data, "foo")], "click here") end, [input("foo"), submit()]).

-----

1 point by partdavid 2509 days ago | link

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:

   handle(_, [{said, Value}|_]) ->
        {Value, {ehtml, {a, [{href, self}], "click me"}}};
   handle(S, [_|R]) ->
        handle(S, R);
   handle([], []) ->
        {[], {ehtml, {form, [{action, self}],
                            [{input, [{type, "text"},
                                      {name, "said"}, {size, 50}], []},
                             {input, [{type, "submit"}]}]}}};
   handle(S, []) ->
        {S, {ehtml, {p, [], io_lib:format("you said: ~s", [S])}}}.
Like many of the other examples, it's lacking the clever HTML generation.

-----

3 points by jchung2008 2515 days ago | link

  <%@ Page Language="C#" ClassName="WebApplication1._Default" %>

  <script runat="server">

    // C# and ASP.NET
    protected void SubmitButton_Click(object sender, EventArgs e)
    {
        MultiView1.ActiveViewIndex = 1;
    }

    protected void ClickHereButton_Click(object sender, EventArgs e)
    {
        SaidLabel.Text = string.Concat("You said: ", SayTextBox.Text);
        MultiView1.ActiveViewIndex = 2;
    }

  </script>

  <html>
  <head runat="server">
    <title></title>
  </head>
  <body>
    <form id="form1" runat="server">
      <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
        <asp:View runat="server">
          <asp:TextBox ID="SayTextBox" runat="server" />
          <asp:Button ID="SubmitButton" runat="server" Text="Submit" OnClick="SubmitButton_Click" />
        </asp:View>
        <asp:View runat="server">
          <asp:LinkButton ID="ClickHereButton" runat="server" Text="Click Here" OnClick="ClickHereButton_Click" />
        </asp:View>
        <asp:View runat="server">
          <asp:Label ID="SaidLabel" runat="server" />
        </asp:View>
      </asp:MultiView>
    </form>
  </body>
  </html>

-----

4 points by amund 2512 days ago | link

Shorter but non-idiomatic C#/ASP.NET:

    <%@ Page Language="C#" %>
    <html>
    <head>
    <title>Said</title>
    </head>
    <body>
        <form id="form" runat="server">
            <% if (!IsPostBack) { %>
                <input name="foo" />
                <input type="submit" />
            <% } else if (Request.Form["foo"] != null) {
                Session["foo"] = Request.Form["foo"]; %>
                <a href="javascript:form.submit()">click here</a>
            <% } else { %>
                you said: <%=Session["foo"]%>
            <% } %>
        </form>
     </body>
    </html>

-----

3 points by brentb 2242 days ago | link

Shorter still, but exactly the same idea:

<%@ Page Language="C#" %> <form id="f" runat="server"> <% var v = Request.Form["x"]; if (!IsPostBack) { %> <input name="x" /><input type="submit" /> <% } else if (v != null) { Session["x"] = v; %> <a href="javascript:f.submit()">click here</a> <% } else { %>you said: <%=Session["x"]%><% } %></form>

-----

7 points by mr-anonymous 2514 days ago | link

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 "&#620;&#618;&#331;k&#618;t" (Tlingit), or Chinese like &#20013;&#22283; (China), what's the output page going to say?

-----

6 points by pg 2514 days ago | link

Why don't you try it and see?

-----

12 points by mr-anonymous 2513 days ago | link

Step 1: figure out where/what "MzScheme" is. Google is my friend, but a link to the site wouldn't hurt.

Step 2: figure out how to get the right version. Not hard, but a step.

Step 3: get the right architecture. Also not hard.

Step 4: run $RIGHT_PATH/bin/mzscheme -m -f as.scm

Step 5: figure out how to run "webapp.arc".

Step 5a: First attempt: "mzscheme -m -f as.scm webapp.arc" doesn't work.

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 (&#8721;) comes back without a problem, as does the traditional Chinese for China (&#20013;&#22283;). 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.

-----

3 points by mr-anonymous 2510 days ago | link

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?

-----

1 point by jmatt 2509 days ago | link

PG has already addressed character sets. arc only supports ascii.

For further information see:

http://arclanguage.org/item?id=391

http://paulgraham.com/arc0.html

JMatt

-----

1 point by mr-anonymous 2509 days ago | link

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.

-----

1 point by mr-anonymous 2509 days ago | link

Now I should be able to speak properly. http://news.ycombinator.com/item?id=111100

XSi! Antligen! Tschuss!

-----

5 points by mr-anonymous 2514 days ago | link

And as you can see, this web server doesn't like non-Latin names either. I wonder if the MorgunblaXiX mentioned Paul Erd&#337;s' trip from San Jose to Koln. I heard he talked about Mobius strips while eating ph&#7903;.

(Just had to try it out.)

-----

5 points by mr-anonymous 2514 days ago | link

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.

-----

3 points by staticshock 2513 days ago | link

would unicode support in arc increase the length of pg's excersize program?

-----

2 points by mr-anonymous 2513 days ago | link

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.)

-----

3 points by namaste 2513 days ago | link

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.

-----

3 points by forgotmypass2 2513 days ago | link

In Python + Werkzeug:

    @expose('/arctest')
    def said(request):
        if "msg" in request.cookies:
            resp = BaseResponse('<html><body><h1>You said:</h1><p>%s</p></body></html>' % request.cookies['msg'], mimetype='text/html')
        elif request.method == 'POST':
            resp = BaseResponse('<html><body>Click <a href="/arctest">here</a>.</body></html>', mimetype='text/html')
            resp.set_cookie("msg", value=request.form.get("msg"))
        else:
            resp = BaseResponse('<html><body><form action="/arctest" method="post"><input type="text" name="msg" /></form></body></html>', mimetype='text/html')
        return resp
Even a newbie that's never seen Python or Werkzeug could make sense of it. I don't think you can say the same of the Arc code, it looks like magic.

-----

4 points by kennytilton 2513 days ago | link

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.

-----

2 points by pg 2513 days ago | link

I think it depends on what you're used to. The Arc version seems a lot more comprehensible to me.

-----

1 point by user8472 2505 days ago | link

Actually, I agree with this one. For me, personally, the Arc solution is more readable. It depends on what one is used to.

The question is what the majority of other people think (since that's what really matters).

-----

3 points by t1m 2513 days ago | link

"Any sufficiently advanced technology is indistinguishable from magic."

http://en.wikipedia.org/wiki/Arthur_C._Clarke#Quotes

-----

2 points by larry_h 2512 days ago | link

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.

-----

3 points by stevecooperorg 2470 days ago | link

C#3 and ASP.NET, using my own library;

    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);
Which is 24 code points, I think?

-----

2 points by chriseidhof 2153 days ago | link

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).

-----

2 points by Seanner27 2134 days ago | link

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.

-----

2 points by tiglionabbit 2482 days ago | link

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

-----

2 points by edw 2463 days ago | link

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.

-----

1 point by partdavid 2482 days ago | link

I'm not sure what session is, there.

-----

1 point by tiglionabbit 2482 days ago | link

Session is a base64-encoded cookie with a secret key for each sinatra app.

-----

2 points by midnightmonster 2513 days ago | link

In JavaScript (with E4X) on Helma (Rhino-based web framework) with nothing else added:

  var body = req.isPost() ?
    ((session.message = req.postParams.say), <a href={req.path}>click here</a>) :
    (res.message ?
      <p>{"you said: "+res.message}</p> :
      <form method="post"><input name="say"/><input type="submit" /></form>);
  res.write(<html><body>{body}</body></html>);
As a bonus, XSS attacks are impossible.

-----

2 points by edw 2478 days ago | link

    ;; 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)

-----

3 points by edw 2476 days ago | link

And now, because you guys worship brevity so...

    (define-handler (said)
      (send-xml
       (form (message)
             `(,(field message) ,(submit "Go"))
             (send-xml (link "click here" (send-xml `(p you said: ,message)))))))

-----

2 points by edw 2455 days ago | link

Now, slightly longer, but emits real XHTML and uses AJAX to place the result in the DOM:

    (define-handler (said)
      (page
       "Arc Challenge"
       (form (message)
             `(,(field message) ,(submit "Go"))
             (page "Almost"
                   (ajax-link "Click here" 'message
                              `(p "You said: " ,message))
                   `((div id message))))))

-----

2 points by edw 2454 days ago | link

Now, slightly shorter, more clear, is AJAXian from start to finish:

    (define-handler (said)
      (page
       "Arc Challenge"
       (ajax-form 'output (message)
                  `(,(field message) ,(submit "Go"))
                  `(p
                    ,(ajax-link "Click here" 'output
                                `(p "You said: " ,message))))
       '((div id output))))

-----

2 points by kismert 2436 days ago | link

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.

-----

1 point by almkglor 2435 days ago | link

I suggest you try looking at the Anarki repository, which has a few tentative structures for balancing, some steering, and a bit of braking.

-----

3 points by eric256 2511 days ago | link

Looks like perl hasn't been represented much.

Why not just the following?

        #!/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.

-----

6 points by lol 2510 days ago | link

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:

print "enter your message";

input a$

print "hit enter here";

input

print "you said: ";a$

i am so impressed now...

-----

2 points by jfm3 2510 days ago | link

So true! The power of the web framework is the power of the web framework. It's almost unrelated to the power of the language substrate!

-----

3 points by xiphorian 2515 days ago | link

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 :)

-----

6 points by pg 2514 days ago | link

What's passed in the url is the id of a closure stored on the server.

-----

3 points by nostrademons 2513 days ago | link

What happens if you need to scale to multiple servers? Or if the server goes down for a reboot?

-----

5 points by pg 2513 days ago | link

You'd do the same sorts of things you'd do in any language.

-----

6 points by nostrademons 2513 days ago | link

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.)

-----

1 point by dc27437 2512 days ago | link

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.

-----

1 point by nostrademons 2512 days ago | link

For basic JSF, you just set a context-param on your web.xml for javax.faces.STATE_SAVING_METHOD = client.

http://forum.java.sun.com/thread.jspa?threadID=594475&me...

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.)

-----

1 point by dc27437 2511 days ago | link

Thanks! I appreciate it.

-----

1 point by xiphorian 2513 days ago | link

Hmmm, I guess I still don't understand :-(

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.

If it satisfies your challenge to say, the data gets to the server by a POST on http://arclanguage.com/first, and then the web server stores the state in a closure and forwards to http://arclanguage.com/second, which displays some things, why would it not be sufficient simply to POST to http://arclanguage.com/second ?

Is the important point the fact that the data _entered_ /first and was used in /second?

Anyway, thanks for your time. I hope you understand this is a genuine question and I am not trying to be pedantic :-)

-----

1 point by bogomipz 2513 days ago | link

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.

-----

4 points by lupisak 2515 days ago | link

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.

-----

1 point by lgriffith 2513 days ago | link

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.

-----

1 point by queensnake 2512 days ago | link

> 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/.

-----

2 points by lgriffith 2512 days ago | link

> 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.

-----

1 point by nunb 2512 days ago | link

PAH! TOSH! PISH-PASH!

-----

2 points by connellybarnes 2514 days ago | link

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.

-----

3 points by rhandom 2511 days ago | link

Here is another Perl one with CGI::Ex::App (with an un-released version with session support).

  use base qw(CGI::Ex::App);
  __PACKAGE__->navigate;

  sub main_file_print  { \"<form method=post><input name=foo><input type=submit></form>" }
  sub main_finalize    { my $self = shift; $self->session($self->form); 1 }
  sub main_next_step   { 'click' }
  sub click_file_print { \"<a href=[%script_name%]/show>click here</a>" }
  sub show_file_print  { \"you said: [% session.foo.html %]" }

-----

1 point by mfex 2513 days ago | link

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.

-----

2 points by t1m 2512 days ago | link

<?php

  session_start();
  
  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $_SESSION['said'] = $_POST['said'];
    echo '<a href="">click here</a>';
  } else {
    if (isset($_SESSION['said'])) {
      echo 'You said: ' . $_SESSION['said'];
      unset($_SESSION['said']);
    } else {
      echo '<form method="post">';
      echo '<input type="text" name="said">';
      echo '<input type="submit">';
      echo '</form>';
    }
  }
  
?>

-----

2 points by namaste 2512 days ago | link

Here's the same thing translated into Ruby + custom Web Framework + Tenjin:

<?rb

  if @w.req.post?
    @w.session['said'] = @w.f('said')
    @w << '<a href="">click here</a>'
  else
    if @w.session['said']
      @w << "You said: #{@w.session['said']}"
      @w.session.unset 'said'
    else
      @w << '<form method="post">'
      @w << '<input type="text" name="said">'
      @w << '<input type="submit">'
      @w << '</form>'
    end
  end

?>

BTW, your example made me spot a bug and fix it and also add the "unset" method to the Session class. Cool enough. Thanks! :-)

-----

2 points by javadesigner 2489 days ago | link

In Molly (molly server pages)

page1.mp: <form action=page2.mp method=post> <input name=input1 type=text> <input type=submit> </form>

page2.mp: <form name=myform action=page3.mp method=post> <input type=hidden name=input1 value='[=req.getParameter("input1")]'> </form> <a href="javascript:document.forms["myform"].submit();">click here</a>

page3.mp: you said: [= req.getParameter("input1") ]

-----

1 point by phil 2513 days ago | link

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.

-----

1 point by user8472 2505 days ago | link

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.]

-----

1 point by namaste 2512 days ago | link

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. :-)

-----

1 point by melevy 2508 days ago | link

The program definition:

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.

(entry "http://localhost:8080/said" (show (input-field a) (submit-button (show (link "Click here" (show (text "You said: " a)))))))

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.

levy

-----

1 point by christantillo 2508 days ago | link

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 :)

-----

1 point by user8472 2505 days ago | link

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.

-----

2 points by colin 2287 days ago | link

This is tcl under Wub.

  Coco init said {r {
    set r [yield [Http Ok+ [yield] [<form> said "[<text> stuff][<submit> ok]"]]]
    Query qvars [Query parse $r] stuff
    return [Http Ok+ [yield [Http Ok+ $r [<a> href . "click here"]]] ["You said: $stuff"]]
  }}

-----

2 points by nicwolff 2508 days ago | link

Perl, HTML::Mason, and XHTML.pm:

<% xhtml( $ARGS{show} && $u->{name} or ( $u->{name} = $ARGS{name} ) ? [ a => { href => '?show=1' }, 'click here' ] : [ form => { method => 'POST' }, map [ input => $_ ], { name => 'name' }, { type => 'submit' } ] ) %>

-----

1 point by davidnicol 2505 days ago | link

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

<html><body><form action="YouSaid/$counter" method="GET"> <input type=text name=foo> <input type=submit></form></body></html> LIGHTNING }, '/YouSaid' => sub {

    my ($foo) = $_{QUERY_STRING} =~ m/foo=(.+)/;

    <<THUNDER
Content-type: text/html

<html><body>you said: $foo </body></html>

THUNDER } };

-----

3 points by lr 2510 days ago | link

A 'correct' solution for Seaside that only requires 14 parse tree nodes: http://www.lukas-renggli.ch/blog/take-the-arc-challenge

-----

2 points by sroccaserra 2390 days ago | link

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.

  | something |
  something := self request: 'Say something'.
  self inform: 'Click here'.
  self inform: 'You said: ' , something

-----

1 point by akkartik 2390 days ago | link

yeah, so cool.

-----

1 point by mattknox 2511 days ago | link

nyclisp, using the web framework I'll have to write when I get home from SF:

  (serve said
    (form1 [link-to-code "click here" (text "you said: " _)]))
(edited for format)

-----

1 point by easykill 2463 days ago | link

Lasso (http://www.lassosoft.com/), no framework:

<html>

<body>

[

session_start( -name='helloworld');

if(session_result == 'new');

		'

		<form method="post">

			<input name="msg" type="text" />

			<input name="go" type="submit" value="go" />

		</form>

		';

	else(action_param('msg'));

		var('msg') = action_param('msg');

		session_addvar('msg', -name='helloworld');

		'<a href="' + response_filepath + '">Click Here</a>';

	else;

		'you said: ' + $msg;

		session_end( -name='helloworld');

	/if;
]

</body>

</html>

-----

3 points by waf 2512 days ago | link

For a slightly more realistic example, try implementing http://www.accursoft.co.uk/waf/ , and see how Arc stacks up against Seaside.

-----

2 points by Joseph_Bui 2508 days ago | link

TCL in AOLserver

  if {[ns_conn method] eq "POST"} {
    ns_set put [ns_conn outputheaders] Set-Cookie "foo=[ns_urlencode [ns_set get [ns_getform] foo]]; Path=/"
    set page "<html><body>said.tcl</body></html>"
  } elseif {[regexp {foo=([^;]*)} [ns_set iget [ns_conn headers] cookie] match foo]} {
    set page "<html><body>you said: [ns_quotehtml [ns_urldecode $foo]]</body></html>"
  } else {
    set page "<html><body><form method='POST' action='said.tcl'><input name='foo'><input type='submit'></body></html>"
  }
  ns_return 200 text/html $page

-----

1 point by squigg 2509 days ago | link

adapted to include the submit button and "you said:" text...

  page <#>{(formlet <#>{input -> y}{submitButton("submit") -> z}</#> yields y) => fun (x) {page <a l:href="{page <#>{stringToXml("you said: " ++ x)}</#>}">Click Here</a>}}</#>
if i space it all out so it's nice and readable:

  page
  <#>
  	{(formlet <#>{input -> y}{submitButton("submit") -> z}</#> yields y)
  		=> fun (x) {
			page <a l:href="{page <#>{stringToXml("you said: " ++ x)}</#>}">Click Here</a>
	}}
  </#>

-----

1 point by abu 2366 days ago | link

PicoLisp - put the following text into a file, set the "executable" bit, run it, and point your browser to port 8080:

   #!bin/picolisp lib.l

   (load "ext.l" "lib/http.l" "lib/xhtml.l" "lib/form.l")

   (de p1 ()
      (app)
      (html 0 "First Page" "lib.css" NIL
         (<post> NIL "@p2"
            (<field> 20 '*Said)
            (<submit> "OK") ) ) )

   (de p2 ()
      (html 0 "Second Page" "lib.css" NIL
         (<href> "Click here" "@p3") ) )

   (de p3 ()
      (html 0 "Third Page" "lib.css" NIL
         "you said: " *Said ) )

   (server 8080 "@p1")

-----

1 point by johnmshea 2511 days ago | link

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.

John

-----

1 point by lgriffith 2511 days ago | link

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.

-----

1 point by fedemart 2318 days ago | link

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.

-----

1 point by squigg 2509 days ago | link

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.

-----

1 point by cut-sea 2490 days ago | link

With Kahua(Continuation Base Framework on Gauche):

   (define (page . args)
     (html/ (apply body/ args)))

   (define-entry (said)
     (define (end say)
       (page (p/ "you said:" say)))
     (define (here say)
       (page (a/cont/ (@@/ (cont (lambda _ (end say)))) "click here")))
     (page
      (form/cont/ (@@/ (cont (entry-lambda (:keyword say) (here say))))
        (input/ (@/ (type "text") (name "say")))
        (input/ (@/ (type "submit") (value "SAY"))))))

   (initialize-main-proc said)

-----

1 point by cut-sea 2483 days ago | link

Kahua Current.

   (use dsl-simple)

   ;; said
   (define-main-page (said)
     (selfish "say"
      (form/cont/ (entry-lambda (:keyword say)
   		 (a/cont/ (cut p/ "You say:" say) "click here"))
           (readln/ say)
           (submit/))))
and below code realize said x 5 parts in a page.

   (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.

-----

2 points by tbourdon 2510 days ago | link

I agree with massung. I enjoy reading PG but I also think his middle name should be "Strawman" as that's how most of his arguments are crafted.

-----

1 point by maximegb 2480 days ago | link

PHP, no framework :

<html>

<?php

if ($_GET['data'])

{

        $id = uniqid().'.html';
        file_put_contents($id,"You said : ".$_GET['data']);
        echo ''.$id.'';
}

else

{ ?>

        <form method="get">
        <input type="text" name="data">
        <input type="submit">
        </form>
<?php } ?>

</html>

Can we consider this a RESTful approach ?

-----

1 point by epicureanideal 2494 days ago | link

// 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

<? ($_POST['foo']) ? print "You said: " . $_POST['foo'] : false; htmlInput('foo'); htmlSubmit(); ?>

-----

2 points by dualogy 2513 days ago | link

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.

-----

1 point by abey 2459 days ago | link

<?php // unvarying HTML elided above if ($foo = $_POST['foo']) { $_SESSION['foo'] = $foo; print '<a href="">click here</a>'; } else if ($foo = $_SESSION['foo']) { print 'you said: ' . $foo; } else { print "<form method='post'><input type='text' name='foo'><input type='submit'></form>"; } ?>

-----

1 point by johnLarson 2154 days ago | link

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

-----

1 point by CarlGundel 2509 days ago | link

Here's a version that uses Run BASIC, a web appserver built on Seaside.

To try it out, paste the code here http://runbasic.com/seaside/go/runbasic?_page=WriteYourOwn

input "Say something"; something$

cls

link #continue, "Click here", [continue]

wait

[continue]

cls

print something$

-----

1 point by tomoyuki28jp 2134 days ago | link

web4r (Common Lisp): http://github.com/tomoyuki28jp/web4r/tree/master

  (defpage said ()
    (form/cont/ (a/cont/ (p/ "You said: " (last-post "foo")) "click here")
      (input-text/ "foo")))

-----

1 point by apoirier 2511 days ago | link

With our soon-to-be-released Python framework:

    class Said(Task):
        def go(self, b):
            said = b.call(View(lambda h: h.form(h.input.action(lambda v: b.answer(v)), h.input(type='submit'))))
            b.call(View(lambda h: h.a('Click here').action(b.answer)))
            b.call(View(lambda h: h.div('You said: ', said)))

-----

1 point by davidclark 2501 days ago | link

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.

http://prophipsi.blogspot.com/2008/02/arc-is-sympton-not-sol...

-----

[deleted]
1 point by bayareaguy 2511 days ago | link

what language is that?

-----

1 point by mattknox 2510 days ago | link

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.

-----

2 points by marschall 2510 days ago | link

With Seaside

    html anchor
        callback: [
            (self request: 'enter something') in: [ :answer |
                self inform: 'you said: ', answer] ];
        with: 'click here'
"dirty in the right way"

update: Damn, can't read. This puts the link first, then the form.

-----

1 point by davidnicol 2505 days ago | link

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.

-----

1 point by melevy 2508 days ago | link

I meant

<pre> (entry "http://localhost:8080/said" (show (input-field a) (submit-button (show (link "Click here" (show (text "You said: " a))))))) </pre>

-----

1 point by squigg 2508 days ago | link

you have to indent the line by 2 spaces or more to get it to show as code.

   like this line here

-----

1 point by eungju 2504 days ago | link

I tried it in Erlang with Seaside-like framework. Not looks pretty but it works :-)

See http://colus.cafe24.com/hgwebdir.cgi/away/rev/857e32993df3

-----

1 point by abey 2459 days ago | link

<input id="in" type="text" /><input type="submit" onclick="window.result='You said: '+document.getElementById('in').value; document.body.innerHTML=''#\'" value="Submit"/>

(it's a big hack, I know)

-----

1 point by atnan 2512 days ago | link

See my submission here:

http://reddit.com/r/programming/info/67gu9/comments/c032wjy

-----

1 point by bash 2509 days ago | link

Bash:

  read text
  read -p clickhere
  echo you said $text

-----

1 point by CarlGundel 2508 days ago | link

Is that a web app?

-----