
Beating the Arc Challenge in Haskell - chriseidhof
http://gist.github.com/260052
======
gruseom
Very nice work, and the code is clear and beautiful to a Haskell illiterate
such as myself. But I have a problem. If your solution involves writing a
library and importing it, have you really written a program with "only 13
nodes total"? If the answer is yes, then why can't I just put _that_ program
into a library, call it from a new program with only 1 or 2 nodes, and say
"look at the size of this!"?

It's not obvious what counts as "the language" and what as "a library". Is
printf part of C?

I suppose one could argue that if a library is general enough to make an
entire class of programs shorter, as opposed to a few specific programs, then
it gets to count as "the language". But that doesn't resolve the question so
much as reword it.

Anyway, this is probably more of an objection to the original challenge than
it is to this lovely Haskell code.

~~~
wheels
Yes, I had the same problem with the initial challenge. I could patch
virtually and programming language implementation such that it would be
possible to write a 5 line web app, but that doesn't really say much about the
language.

~~~
pg
The criterion for tests like this is whether you're relying on stuff you'd
expect any language already to have, or not. Features fall on a continuum in
that respect. You'd expect any language to be able to add two numbers; you
couldn't expect a language to have a library for parsing some format you'd
just invented; other features fall somewhere between these extremes.

I honestly didn't think any of the things I did in the original Arc challenge
required features that wouldn't already have existed in a language that had
been used for web programming. All it does is ask the user to input something,
then show it on another page. It's practically the hello world of stateful web
apps. It seemed reasonable to expect any language that had been used for web
apps would already have had convenient libraries for doing such basic things.

~~~
wheels
I did a quick Ruby version just to serve a basis for my point:

<http://pastie.org/750268>

The reason I posted that was precisely because of the point where Arc shines:
no inline HTML. But in practice, I want to avoid putting my presentation logic
inline with my programming logic anyway.

And I fear that's the problem with the challenge; it seems to be highlighting
the wrong kind of simplicity. I could add a few utility functions to make Ruby
able to generate that HTML with a tiny amount of code, but I wouldn't ever
actually use those functions in a real program.

Edit: Out of curiosity, I just implemented a pseudo-concision-obsessive
"library" so see how a Ruby version might look:

<http://pastie.org/750269>

I'll note that Rails _does_ have things like those that are often used
templates.

Another edit: Moved code samples to pastie for readability.

~~~
DougBTX
The other matter of style is whether the order of the code matches the order
in which the user interacts with it.

The three steps in user experience order:

    
    
      first:  a form to accept input,
      second: a link to click
      third:  a printout of the input
    

Here are the orders used in code:

    
    
      Ruby+sinatra:   third, first, second
      arc version 1:  third, second, first (original)
      arc version 2:  second, third, first (May 2009)
      Haskell+custom: first, second, third
    

Interesting results, I think Haskell wins here. Though technically it fails
the challenge (the arc version 2 example fails too) since the supporting
library was written after the challenge was considered by the author of the
example.

~~~
draegtun
The Perl+Continuity solution also uses "first, second & third" style and does
it nicely without sessions and adheres to challenge by being an established
library: <http://arclanguage.org/item?id=805>

Here is a version using the HTML::AsSubs module:

    
    
        use Continuity;
        use HTML::AsSubs;
        Continuity->new->loop; # This starts the webserver
    
        sub main {
            my $request = shift;
            my $p = 'foo';
    
            $request->print( aform( $p )->as_HTML );
            my $foo = $request->next->param( $p );
            $request->print( w_link()->as_HTML );
            $request->next->print( "You said: $foo" );
        }
    
        sub aform {
            form( 
                input( { type => 'text', name => $_[0] } ), 
                input( { type => 'submit'} )
            );
        }
    
        sub w_link { a( { href => '.' }, 'Click Here' ) }

------
KirinDave
I'm not sure haskell won here. I think maybe programming with monads won.

No, really. Web programming and continuation monads seem to have really it it
off together. When you add them to another language, you get similar results
(e.g., <http://intensivesystems.net/tutorials/web_sessions.html>).

~~~
nostrademons
For pure shortness, I think HTML + JavaScript won here:

<http://arclanguage.org/item?id=979>

The Haskell example is interesting, though, because it shows a working webapp
in Haskell, and it's not even that scary.

~~~
KirinDave
Except that these two code examples do two very different things. The Haskell
version actually uses a continuation to run multiple requests The Javascript
version modifies local mutable state. The Haskell version does it via server
interaction, which makes it much more useful to modern web apps.

------
parenthesis
Haskell is that rare beast: a language that Lispers may envy.

~~~
jganetsk
Isn't this problematic? Lispers fling around lots of superlatives, as if their
favorite language will never be dethroned from its position atop the language
kingdom. And they always felt justified about this.

Shouldn't we confront them about this?

I do think there's a science of programming languages which can inform us how
to best design the tools we use. Isn't it just possible that these Hindley-
Milner languages are the next step in the progression that LISP started?

Look at the transition from Newtonian to Einsteinian mechanics. They were both
great at the time of release, but one is clearly a step forward. The former is
usually embeddable within the latter, but edge cases in the former don't quite
work the same in the latter. And some cases in the former are just downright
illegal in the latter. And unfortunately, the latter is a lot more complex for
the power users. But, the latter is more expressive, teaches us more about the
world... and enables completely new possibilities like nuclear fusion/fission.

This is like the difference between dynamic and static typing. Dynamic typing
is certainly embeddable within Haskell. Just define a data type Dyn which is
the sum of types Int, String, Float, [Dyn], and Dyn -> Dyn. You will see how
much LISP is possible without much added syntactic cruft. But Haskell users
don't need and want to operate in this way, because they can leverage the
benefits of static typing.

Remember, these things don't come from whims of clever people or corporations.
LISP and Haskell both come from research of very fundamental ideas. his is
much more like real science than social science. And we should keep our eyes
open to the future of this field, instead of proclaiming that we're finally
done with our "100 year language".

~~~
silentbicycle
There isn't some unified continuum of power in languages, though. There are
some problems for which a H-M-ish type system is a tremendous advantage, and
there are others for which it gets in the way. Same with lazy evaluation - it
can make a problem much simpler _or_ much harder to reason about. Sometimes
using a language built from the ground up to support distributed and
concurrent programming (e.g. Erlang), unification and backtracking (Prolog),
or constraint programming is the right tool for the job. Sometimes being able
to work close to the Unix kernel or run on an embedded system without an OS is
more important.

Languages are a means to an end -- they help manage complexity while solving a
problem. There's value in being able to extend a language's semantics to
support a superset of several major language families, but unless handled very
carefully, it can turn the host language into a sprawling mess in the process.

~~~
nradov
That's why we need a language with a scalable type system that supports both
static and dynamic typing. (Actually VB had that years ago with Variant data
types, but the rest of the type system was horrible.)

~~~
silentbicycle
I suspect it might be fruitful to do static analysis for constraints, with
type identity as just one attribute. While inferring that X is an int is
useful, inferring that it's an int which is always positive and less than 256
would allow a lot of other optimizations. Even if an inference engine can't
completely prove something is always a (string * int list) pair, it would
still be useful to know that it's (string * (either int or double) list), and
the list cannot be empty. Etc. Type declarations or inference are a bit all-
or-nothing, and I think being able to read through the properties that the
compiler could infer (or at least confirm) would help find bugs, suggest
optimizations, etc.

If I ever get past the first dozen projects on my list, I'd like to write a
compiler for a dialect of Prolog designed with constraint analysis in mind. (I
also need to read more about what's already been tried, first - this is just
me being curious about how far constraint analysis could go and wondering out
loud.) It would be tricky, but more feasible with Prolog-like semantics than
in, say, C.

------
chriseidhof
By the way, I would be really interested in Arc's solution to the second
program I've shown.

------
JulianMorrison
I truly don't get what this is trying to demonstrate?

I mean, I could write a one word web app:

    
    
        dwim
    

(Details of the standard library implementation are left as an exercise.)

------
anc2020
I'm a bit late here, but if you want to really smash the challenge, you can
use Lisp style metaprogramming with my Hasp program. Something like this is
possible (10 nodes):

    
    
        def arc
          with->>= name input
            link "click here"
            display++ "you said: " name

------
albertcardona
I am trying to run the haskell code in Ubuntu 9.10 with ghc6, but:

    
    
      Failed to load interface for `Control.Applicative.Error':
          locations searched:
            Control/Applicative/Error.hi
            Control/Applicative/Error.hi-boot
    

Which library am I missing? apt-file search for Control/Applicative/Error.hi
yielded none.

Help appreciated from all HN-reading haskell'ers.

~~~
chriseidhof
do a "cabal install applicative-extras", that should help.

