
Syntax Matters? (2012) - ingve
http://smallcultfollowing.com/babysteps/blog/2012/04/15/syntax-matters-dot-dot-dot/
======
hzhou321
Yes, syntax matters. What is in question is why syntax has to be so tightly
bound to intrinsics of a language? I think it is possible to have a meta layer
above any programming language that can bridge particular flavor of the syntax
of your choice to the underlying language. In fact, I have been practicing
that for the last 10 years (MyDef).

To illustrate what I mean by meta layer, the preprocessor (macros) is a
(crude) example of meta layer that is (in fact) independent from the C
language. In fact, M4 can and has been used on top of many independent
languages. But to have a fuller syntax layer, we'll need a more sophisticated
(not necessarily more complex) system than M4.

~~~
saurik
While technically independent of the C language, the C pre-processor has the
idea of C tokens baked into its core (such as refusing to ## together two
things that would result in invalid C), which has caused some people (such as
Haskell) who relied on the ability of some C pre-processors to sort of handle
things that are not C, they have been burned as of late when dealing with ones
that have refused to implement extra modes that allow it to process arbitrary
text (such as clang).

The preprocessor is essentially specified so as to be able to be efficiently
implemented inside the tokenizer of a unified C compiler stack (hence why
token pasting isn't textual): it isn't really "independent from the C
language". In contrast, m4 is fundamentally more "independent" than the C
preprocessor, in that (as far as I know... my experience with m4 is much less,
and mostly with respect to amateur-level autoconf work) it doesn't assume
anything about the underlying text.

~~~
geofft
One of the curious things about clean layers of abstraction is how often
they're counterproductive in practice.

------
brudgers
I've been in love with Lisp since the 1980's. But recently I came to the
realization that Lisp syntax creates drag on embedded Domain Specific
Languages because they wind up with the aesthetics of Lisp syntax, and
sometimes that's not really the right design...e.g. implementing RSpec in
Clojure loses any potential readability advantages that come with somewhat
natural language like phrasing: surrounded by parenthesis and with terms
reordered, the important point of human readability with little friction is
lost.

That's not to say that there are not advantages to Lisp syntax. There are and
they may outweigh other costs, but I can understand why people object to Lisp
aesthetics.

~~~
nickbauman
RSpec remains one of the more controversial testing approaches in the Ruby
community. DHH, for example, consistently knocks it. In this I think Hickey's
comment remains gold: "there is no syntax you can create that will improve
upon pure data".

But then I used midje (a Clojure analog to RSpec) with glee, so, by gum, my
position is even incoherent.

~~~
brudgers
My impression is that Mr. Hanson knocks a lot of things in hopes of finding
someone to argue with...and here I am knocking. On the other hand, I think Mr.
Hickey is insightful about many things, yet perhaps inconsistently, I still
believe that code is potentially the most precise way of describing
information.

------
pmontra
Yes, syntax matters but I suspect that how we react to a language depends on
which languages we come from. Familiar syntaxes usually make learning a new
language easier.

Go through the code at
[http://rosettacode.org/wiki/Letter_frequency](http://rosettacode.org/wiki/Letter_frequency)
and whatever your background is you'll see some programs that will look almost
incomprehensible and others that you can understand even if you don't know the
language they are written into.

And then there is the aesthetic: some of those programs look beatiful, some
look ugly. That matters too.

~~~
Avshalom
Completely unrelated to the topic but I'm struck by the Ada solution. So many
other languages either make a map or make an array and then back out
characters from numeric indexes.

Ada just lets you use characters as indexes. Ada will actually let you use
damn near anything as an index. I use enumerated types to build lookup tables
for stuff a lot when playing around with it.

~~~
pmontra
Many languages have associative arrays that can take any data type as index. A
Ruby example, intentionally written to make it easy to read (and maintain):

    
    
        array_of_characters = ["a", "b", "a", "c"]
        count = {}
        array_of_characters.each do |character|
          if count.has_key?(character)
            count[character] += 1
          else
            count[character] = 1
          end
        end
        count
         => {"a"=>2, "b"=>1, "c"=>1}
    

The one on rosettacode is smarter but it takes some time to be understood.

~~~
Avshalom
Ada has associative arrays as well. But it will also let you have normal old
boring arrays with enums, chars and modular types as indexes.

~~~
pmontra
Interesting. Maybe naively, I always assumed that the difference between those
two data structures is that an array has integer indices to be mapped
trivially to RAM and CPU index registers, and associative arrays are hash
tables. I googled this
[https://en.wikibooks.org/wiki/Ada_Programming/Types/array](https://en.wikibooks.org/wiki/Ada_Programming/Types/array)
and it seems that arrays in Ada can have only discrete indices. Example from
there

    
    
        type Character_Counter is array (Character) of Natural;
    

I guess that the compiler maps Character to integers and uses index registers
to access RAM. That would be character.ord in Ruby and in many other languages
but it's a lower level construct.

~~~
Avshalom
I can for instance do

    
    
      type throw is (ROCK, PAPER, SCISSORS);
      type result is (ME, YOU, DRAW);
    
      winner : array (throw, throw) of result := (
        (DRAW, YOU, ME),
        (YOU, DRAW, ME),
        (ME, YOU, DRAW));
    
      YOU = winner(ROCK, PAPER); --would evaluate to true
    

because types are cool.

------
brudgers
Previous:
[https://news.ycombinator.com/item?id=3847605](https://news.ycombinator.com/item?id=3847605)

------
jrcii
When I was a young child I'd type random things into the Apple IIs at school
and they'd say, "? SYNTAX ERROR" "What is syntax?" I wondered. Decades later,
I still make syntax errors.

------
CuriousSkeptic
wether names are part of signatures is not merely syntax, as the article
argues, but very much a semantic issue. It intrudces a translation step from
one environment to another by translating selected parts of it to and from a
list. Which forces us into odd semantics of order in places where order
usually does not matter. While at the same time throwing away any semantics of
naming, usually much more important.

Besides the usability issue of introducing the wrong order as noted by the
article, it also makes a mess of things like partial application and
composition. Consider a language like Haskell where this is a fundamental part
how you design function signatures. There is a function for swapping argument
order just to make this work. [1]

Had the semantics of the language instead considers binding names more
important than ordering there would be no need for that. A -> B -> C would
already equal B -> A -> C. In fact you could throw away things like curry and
uncurry too if you treated tuples as records, {A,B} -> C would also equal A ->
B -> C

Of course now binding names would be much more important than ordering, so
something like a rename function would probably still be needed. But it
couldn't be much worse than all the workarounds for ordering currently
introduced.

[1] [http://stackoverflow.com/questions/12129029/how-to-change-
th...](http://stackoverflow.com/questions/12129029/how-to-change-the-order-of-
arguments)

------
innocentoldguy
Syntax matters, but I think it is subjective. I know a lot of engineers who
like the syntax in Java, for instance, but I hate it. To me, it is needlessly
cumbersome and wordy, and I derive no joy from programming in it. I know a lot
of engineers who hate Python's syntax, but I like it because it encourages
consistency. My favorite languages at the moment are Ruby, Elixir, Python,
Elm, and Haskell, and syntax plays a large role in that opinion.

------
0xcde4c3db
Syntax matters for ease of writing code transformation and analysis tools
(e.g. syntax checking, refactoring, formatting, generation) that consistently
and efficiently work. IIRC, it was a design goal of the Go syntax to make such
tools simpler. C++, on the other hand, has a reputation of being impossible to
parse with anything except a C++ compiler.

------
raymondh
It is interesting that designers universally agree that fonts matter, but
programmers can't agree on whether syntax matters.

~~~
sametmax
Some dev does.

E.G: it's very important for the Python community and it one of the selling
point of the language.

I tend to think that syntax IS very important for the very same reasons Guido
does:

We are basically typers. Making a language with many special charaters hard to
type on your keyboard is going to make you work more, no matter how you slice
it.

Yet we read code 100x more times than we read it. Hence reading must be
efficient.

But there is a soft spot between a code being too long or too concise, either
abuse making it harder to scan. And we scan a lot.

Take the colon in Python blocks for example, it is syntaxically useless:

    
    
         if stuff: # <- the compiler could do without this
             foo()
    

It's main appeal is to improve your hability to scan the code:

\- your brain can quickly catch all starting block in all nested these
indents. \- it's way easier to type than "{" \- you use it the same way you
would do it while writting english (see this very post). It's familiar, and
you are already trained by grade school to scan it.

So yes, since you will spend 8h a day reading source code, syntax is of the
most importance, so you can express enough, and yet read it without too much
though.

------
Chris_Newton
I agree that semantics are usually more important than syntax in a programming
language, but surely syntax can still make a huge difference to both
productivity and reliability. Let’s consider a simple example: taking a
sequence of integers, and printing out those which are less than 5.

Here is how we might do that in Python, a language well known for its clean
and simple syntax:

    
    
        xs = [1, 2, 3, 4, 5, 6, 7]
        ys = filter(lambda x: x < 5, xs)
        for y in ys:
            print y
    

That seems pretty straightforward to me, though the lambda expression is a
little clumsy compared to a language like Haskell that specialises in
combining functions neatly:

    
    
        main :: IO ()
        main = do
            let xs = [1, 2, 3, 4, 5, 6, 7]
            let ys = filter (<5) xs
            sequence_ $ map (putStrLn . show) ys
    

The output step in Haskell is a little awkward, as we have to be explicit
about sequencing the effects, but this is more of a semantic issue than a
syntactic one.

Now, let’s look at a C++ version. The C++ standard library has provided built-
in filtering algorithms ever since its first standard back in the ’90s, but
the syntax if you tried to actually use them was so bad that it took me about
15 minutes to even get this example to work:

    
    
        #include <algorithm>
        #include <functional>
        #include <iterator>
        #include <vector>
        #include <iostream>
        
        #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(*x))
        
        int main()
        {
            const int xs[] = {1, 2, 3, 4, 5, 6, 7};
            std::vector<int> ys;
            std::remove_copy_if(xs, xs + ARRAY_LENGTH(xs),
                std::back_inserter(ys),
                std::not1(std::bind2nd(std::less<int>(), 5)));
            std::copy(ys.begin(), ys.end(), std::ostream_iterator<int>(std::cout, "\n"));
        }
    

This was an attempt to show a like-for-like comparison, though it’s a little
unfair because it also shows the semantic problem of distinguishing between
built-in arrays and the vector container, and it needs a lot of explicit
headers where Python and Haskell both happen to make their filter algorithms
available as standard. Still, just look at that remove_copy_if mess.
Unsurprisingly, approximately no-one ever actually wrote C++ like this for
real.

We could tidy that up with a few of the more recent improvements, but C++ is
still hopelessly clumsy when it comes to employing standard library
algorithms:

    
    
        #include <algorithm>
        #include <iterator>
        #include <vector>
        #include <iostream>
        
        int main()
        {
            const std::vector<int> xs = {1, 2, 3, 4, 5, 6, 7};
            std::vector<int> ys;
            std::copy_if(xs.begin(), xs.end(),
                std::back_inserter(ys),
                [](int x){ return x < 5; });
            std::copy(ys.begin(), ys.end(), std::ostream_iterator<int>(std::cout, "\n"));
        }
    

In reality, I suspect many C++ programmers are still just going to write
C-style for loops instead for simple tasks like these, and who could blame
them? The syntax for the other tools is so bad that programmers will avoid
using them, even though the underlying semantics of that copy_if are no
different to the clean one-liners in Python and Haskell.

~~~
dllthomas
The "awkward" Haskell line could be rewritten:

    
    
        mapM_ print ys

~~~
Chris_Newton
Yes, that would have been a better choice. Thanks.

~~~
dllthomas
Using sequence_ and map can be argued on the grounds that mapM_ is only
recently visible in Prelude in GHC (and not so in the Haskell standard), but
using it is probably more idiomatic and certainly clearer.

~~~
Chris_Newton
I think both changes (mapM_ and print) are certainly clearer and probably more
idiomatic today. Coincidentally, I wrote almost exactly your suggested line of
code only yesterday for another project, but those simplifications didn’t
occur to me as I was writing my earlier post, perhaps because I reached the
original form via some edits to make all the examples produce equivalent
output. I’d love to say I was just trying to be careful about portability in
this case, but I’m afraid it was nothing so clever. :-)

------
PaulHoule
Prolog is homoiconic.

------
killercup
Please note that this was written in 2012.

~~~
Gankro
I almost pinged Niko to be like "you know Java has closures right?" and then I
saw him mention `ret` and was like "oh this is ancient, ok".

