

My Language Is More Agile Than Yours: A Study of Arc - mqt
http://docs.google.com/Present?docid=dg93d8cv_68c2xw9jd5&skipauth=true

======
axod

      >> printIfNotNull(foo.getBar().getName()); // pointless method
    
      >> in arc ...
      >> ... aif is a macro that expands this code to
      >>
      >>(let it ((foo 'bar) 'name)
      >>  (if it
      >>      (print it)))
    

Why is a macro not pointless, while a method/function apparently is? I don't
think that makes sense personally. Surely a macro is just a function that's
been inlined - which may be better, depending on if you're optimizing for
speed or size. Anyone explain?

Also, I'm not sure if this was being serious or not:

    
    
      >> Priorities in language design
      >> A Language Should ...
    

If it was serious, it completely depends on what problem you're trying to
solve as to what priorities you're going to have.

~~~
andreyf
Because aif works with any expression, not just print. Granted, in a
functional language like JS, one could say:

    
    
        ifNotNull(foo.getBar().getName(), function (it) { print it; });
    

but "function (it)" and all of the "().{}" are still blub. As PG points out on
the arclanguage forums [1], an even simpler way of putting it is:

    
    
        (only.print foo!bar!name)
    

Which, without the Arc-specific syntax sugar, is:

    
    
        ((only print) ((foo 'bar) 'name))
    

So "only" is a modifier that makes a verb (function) happen only if its
argument is truthy (think Python decorators).

Note to PG: the dot operator is undocumented, and f.g.h.3 does not at all what
I expect - looks like it does (f g h 3), whereas I expected (f (g (h 3))), in
the spirit of Python decorators.

1\. <http://arclanguage.org/item?id=9350>

~~~
axod
In js, if printIt is a function already defined, you don't need the
function(it){}

ifNotNull(foo.getBar().getName(), printIt);

Granted, in java passing functions around is more of a pain.

~~~
klipt
Yep, a lot of the features mentioned in the presentation are possible with
just first class functions, although the syntax will be messier (but arguably
more consistent).

The strength and danger of macros is in allowing you to create new syntax,
skipping all the extra blub that made your code conform (consistently) to the
original parser. What I'd like to know though is how much humans depend on
that blub.

It's one thing to write your own macros and use them consistently, or even for
a group of people to come up with macros and use them consistently. But if
people work independently, what happens when you read someone else's code and
find they've come up with macros to do the same things as yours do, but with
different names and different usage conventions?

Wouldn't that be a bit like having every scientist invent their own jargon?

Either humans can learn to translate that kind of thing on the fly, or they
can't, and lisp with heavy macro usage will only ever be useful as a solo (or
small group) language.

~~~
gruseom
_what happens when you read someone else's code and find they've come up with
macros to do the same things as yours do, but with different names and
different usage conventions? Wouldn't that be a bit like having every
scientist invent their own jargon?_

You can replace the word "macros" with "functions" and your point would be the
same. Consistency in naming and usage is part of good programming, but the
point is orthogonal to macros.

Edit: actually, it's not orthogonal: macros make it easier to be consistent
because they let you write less code, so there's less code to contradict
itself.

~~~
d0mine
macros can introduce new syntax, functions do not.

~~~
gruseom
So? If you don't understand what a function does, you read it; if you don't
understand what a macro does, you read it. This whole "macros make new syntax
so they make code unintelligible" thing is an argument made by the ignorant,
for the ignorant.

The vast majority of Lisp macros don't create new syntax anyway. They
integrate seamlessly into the s-expression format. That way they blend in
nicely with the rest of a program and it's easy to write other macros on top
of them. An experienced Lisp programmer would only introduce new syntax when
there is a big win in clarity - big enough to justify breaking with the
surrounding structure and certainly big enough to make the above objection
pointless.

By the way, one of the _real_ problems with Lisp macros is that it can be hard
to tell a macro call apart from a function call. (Various techniques have been
developed to deal with this, such as indentation and naming conventions.) If
macros were so syntactically convoluting this would hardly be an issue.

------
silentbicycle
I think it's impossible to have any kind of meaningful conversation about
language design when one side is just writing off everything they don't like
as "blub". It's incredibly condescending, no better than people reflexively
writing off Lisp users as "lisp weenies".

~~~
gcv
The "blub" argument fairly applies just as much to Lisp as it does to, e.g.,
Java. For example, (any dialect of) Lisp is my blub language, as I understand
Lisp quite well. I do not understand Haskell to anywhere near the same extent.
I've tried to learn it several times, but monad transformers consistently make
my eyes glaze over. I am perfectly comfortable admitting this, and I intend to
try harder to understand programming language concepts with which I am
unfamiliar, rather than write off Haskell advocates as condescending.

~~~
silentbicycle
I get what you're saying, but I think dropping the "blub" term would make
everybody better off. Using "blub" intrinsically frames the conversation in
terms of one language being "the stupid one used by the stupid people", and
that's likely to be counterproductive. The word _always_ has that baggage.

It's not a linear continuum, with some "most powerful language" at the top.
Different styles of languages have different strong points and weak points.

------
patio11
The point about string concatenation is, umm, I don't want to say "wrong"
because wrong sounds argumentative so I think I'll pick a circumlocution like
"contrary to fact" or "a bedtime story to scare CS101 students with, second
only to 'use left shift instead of dividing by two, it is faster'".

Also in modern Java (and Ruby) projects you can typically write:

logger.debug("This is a stupidly expensive calculation: " +
stupidlyExpensiveCalculation())

and it will get executed regardless and logged only if your logging settings,
which are configured elsewhere and can be toggled with a mouseclick, are set
to log the debug level.

Now, don't get me wrong, Arc's solution of defining a macro which checks to
see whether your debug setting, which is defined elsewhere and can be toggled
with a mouseclick, is on or not is a good idea. Its just not a revolutionary
new idea for us wizened Big Freaking Enterprise App Ugly Language Programmers.
Nor was it really that new in the halycon days of yore when Log4j crawled out
of the primordial ooze.

~~~
gaius
We get this for free in Haskell.

~~~
klipt
Not exactly free ... laziness and the resulting thunks do use up extra
processing time and memory. But in many cases it is a price worth paying.

<http://www.cs.chalmers.se/~rjmh/Papers/whyfp.html>

------
matth2
If it takes X characters to achieve something in language A, and X*4 to
achieve the same thing in BLUB, I don't think this means BLUB is 4 times
slower to develop in than A.

When I develop software, most of the big delays, and excuses for
procrastination are in forcing my brain into action over the higher level
concepts. I find a lot of the extra lines of code required when using BLUB can
be written at high speed, with little "hurt" to the brain.

I'm not saying BLUB is just as fast to develop with, I'm just saying the
benefit isn't as good as number of lines ratio good.

~~~
req2
It's not in writing 40 lines that something like "blub" hurts, it's in reading
it afterward. As long as you don't end up in Perl one liners, shorter code
will tend to better highlight the relevant content (e.g., there are four keys
that perform one of four things).

------
moe
I like this PG quote from slide 24 a lot:

    
    
       "One way to design a language is to just write down the program you'd
        like to be able to write, regardless of whether there is a compiler that
        can translate it or hardware that can run it."
    

Test driven really works, even for language development.

------
andreyf
I like the argument, especially using the word "blub" to describe the cruft
that is inherent to the language, but I have to question how much it's
preaching to the choir, and how much will penetrate people who don't already
agree.

Maybe it's indicative of my crowd and not the community at large, but even the
smartest people seem to hit up Haskell-for-fun and Python-in-practice over
Arc. It's hard to call them blub-programmers, since they're genuinely curious,
hard-working people...

~~~
klipt
Haskell isn't exactly blub; between the first class functions and the custom
monads it's quite possible to create custom languages. E.g. the parsing
library Parsec:

<http://book.realworldhaskell.org/read/using-parsec.html>

------
Tritis
Anything Arc does better than Clojure that would be enough to offset the
benefit of running on the JVM and full java interop?

~~~
jimbokun
A Clojure slogan could be "The Java De-Blubber!"

~~~
theoneill
I believe it's called flensing.

~~~
Tritis
I learned a new word today.

------
richcollins
Code generation isn't as powerful as late binding. Lisps would be more
interesting if you could easily manipulate environments, and set the
environment that functions are evaluated in. In this regard, Arc isn't as
agile as some OO languages (Io for instance).

~~~
stcredzero
Does this mean that you can change the meanings of your language's "words" for
specific "contexts?" (Context meaning the semantic thing, not a stack frame,
though that could be the implementation.)

~~~
richcollins
Yes. The meaning of symbols within a closure can't change after creation. They
are always bound to the same variable.

In Io, the meanings of symbols are determined when the block is activated.

~~~
stcredzero
I'll have to take a look at Io. I was just thinking of making contexts into
semantic contexts by having users specify what the verbs mean, not just the
nouns.

~~~
richcollins
Io has no "nouns" or keywords. Everything in Io is a verb (a message send).
Also, see <http://news.ycombinator.com/item?id=630906>

~~~
stcredzero
A "getter" is just a synonym for something that's a noun.

------
sker
Is this similar for all the dialects of Lisp? Because if it is, I might start
learning one tonight.

~~~
Raphael_Amiard
The macro definition abilities is mostly similar for most popular lisps
around, Common Lisp, Scheme, Clojure

------
psranga
A lot of the stuff presented here is mainstream practice in Tcl (e.g., new
constructs, dbg evaluation). Tcl is seriously underappreciated language.

In my programming whenever I see myself writing blubber (I like that word), I
just factor it out into a separate procedure and give it a descriptive name.

So when I'm reading the code of the calling routine, I rarely see blubber;
it's contained in simple routines that are easy to read/verify.

Most of the examples here amount to thoughtful library development. With a
good library, C++ and macro processor can also many of these things.

Here's a version of aif that works for most cases (I'm not claiming that C has
the expressive power of Lisp):

    
    
        #define aif(expr, val, code...) \
          { if ((expr) != (val)) { \
              code ; \
            } \
          }
    

IMHO, this is good enough for blubber reduction.

~~~
dreish
That's not aif at all. In fact, you can't even do it for "most cases" in C
because you don't know what type expr will evaluate to, but if we could cheat
and assume it will be an int:

    
    
      #define aif(expr, code)  \
         {                     \
         int it = (expr);      \
         if (it)               \
            {                  \
            code;              \
            }                  \
         }
    

But this is still not close because in a Lisp, if is an expression that
returns a value. We can't use the ternary operator because then there's no
place to declare the "it" variable. You can't have an expression that declares
a variable local to that expression in C no matter how hard you try.

And the above lacks the "do {} while (0)" weirdness needed to make a cpp macro
behave syntactically roughly like a function.

~~~
psranga
I agree. My bad. I appreciate the clarificaiton.

------
stevedekorte
From the slides, it looks like Arc dumps lots of short words in to one giant
global namespace where they can easily collide with local names. Is this
correct?

For example, it looks like Arc uses the "t" as a global true value. Is it
really safe to assume "t" wouldn't be accidentally used as a local variable?

~~~
randallsquared
Well, T has been the true value in Common Lisp, at least, and I don't remember
it ever causing me a problem. Of course, in CL, you can shadow it in your own
namespace, and that hasn't been possible in Arc without writing a namespace
library first. :)

------
10ren
Some other points of view here:
[http://www.reddit.com/r/programming/comments/8nuhn/my_langua...](http://www.reddit.com/r/programming/comments/8nuhn/my_language_is_smaller_than_yours_arc/)

------
st3fan
This presentation could have been about Clojure instead. Just s/Arc/Clojure/

------
illumen
Can't do much without batteries.

------
vlisivka
Original blub code should look like that: frame.addKeyListener(new
KeyListener() { public void keyTyped(KeyEvent event) { switch
(event.getKeyChar()) { case 'j': drop(); break; case 'h': move(-1); break;
case 'k': move(1); break; case 'u': rotate(); break; } }

    
    
          public void keyPressed(KeyEvent event) {
          }
    
          public void keyReleased(KeyEvent event) {
          }
        });
    

About Arc code:

    
    
        (on-key-press frame
          'u     (rotate-shape)
          'j     (drop-shape)
          'h     (move-shape -1)
          'k     (move-shape 1))
    

Is this code ADDS NEW key event handler OR OVERRIDES old one? When it is
invoked - when key is PRESSED or when key is TYPED?

~~~
illumen
ah, events as function calls. How so terribly 50's.

here's a way where events are objects... rather than an invocation. Much more
flexible.

a python + pygame example...

cu,

key_handlers= dict(u=rotate, j=drop, h=lambda : move(-1), k=lambda : move(-1))

buttons = {1:'u', 2:'j', 3:'h', 4:'k'}

for e in pygame.event.get(): if e.type == KEYDOWN: key_handlers[e.key]()

    
    
        elif e.type == JOYBUTTONDOWN:
            key_handlers[buttons[e.button]]()
    
        elif e.type in [QUIT, MOUSEBUTTONDOWN, HTTPD]:
            quit()

~~~
stcredzero
The grandparent code is much easier to understand.

~~~
illumen
The claim is Arc is agile and short... The Arc one has too many ()() all over
the place.

The arc one is also less powerful, because it uses different function
invocations for different types of events.

It's also likely more people will understand the python+pygame version. More
people do currently understand the python version... hardly anyone uses Arc.

You need to understand non-common concepts. You also need to understand how to
create functions - rather than just call functions.

Does that Arc one mean when the key down happens, or when a key up happens? Or
maybe it means in between the key press and key down. Also, when will that
code be called?

It's magic. Will it be called from a separate thread, or some other Magic
time... before or after the screen is to be updated? What else is going on at
that time in the program? With the Arc version you have no idea... it's not
explicit.

Also Arc doesn't have first class events. It could have yes, but it doesn't.

ps. the python code above was mangled by the buggy Arc program running this
forum.

Here is the code redone for simplicity without the extra shortness and power
expressed in the other version.

.

for e in pygame.event.get():

    
    
        if e.type == KEYDOWN: 
            if e.unicode == 'u':
                shape.rotate()
            elif e.unicode == 'j':
                shape.drop()
            elif e.unicode == 'h':
                shape.move(-1)
            elif e.unicode == 'k':
                shape.move(1)
    

Or the declarative python version:

.

dict( u=rotate , j=drop , h=lambda:move(-1) , k=lambda:move(-1))

~~~
stcredzero
_The arc one is also less powerful, because it uses different function
invocations for different types of events._

Actually, that would make it more powerful. You can use polymorphism.

 _It's also likely more people will understand the python+pygame version. More
people do currently understand the python version... hardly anyone uses Arc._

Missing the point. Conciseness is not best measured by eyes familiar with the
language/library. It's best evaluated from the POV of the uninitiated. I have
to parse and run a model to understand your first version. Your second version
is as easily understandable as the Arc example in one way, but it's got a lot
more pollution and an enforced higher line count.

I am answering as someone not well versed in either Python or Arc. (Actually a
little more familiar with Python.)

