

Lisp for C++ programmers - ProgC
http://prog-elisp.blogspot.ru/2013/10/lisp-for-c-programmers.html

======
rayiner
It seems as if every new iteration of C++ proves Greenspun more and more
correct. The standards committee has been trying very hard to push templates
as an "ad hoc, informally-specified, bug-ridden, slow implementation" of
macros.

As for Lisp syntax being ugly, I beg to differ. (Common) Lisp syntax is
somewhat arcane, with terminology and abbreviations that aren't common these
days, but it reads pretty smoothly.

You know what's ugly? C++ lambda syntax:

    
    
        [=]() mutable throw() -> int
    

At some point C++ started following Perl down the road of using as many
symbols above the number keys as possible. _That 's_ ugly.

~~~
detrino
You are being intentionally dishonest by using the most verbose lambda
expression you can think of. It would be _very_ unusual to write something
like that. You even managed to include one of the rare deprecated language
features.

~~~
rayiner
The example is from the first hit for a Google search for "C++ lambda syntax"
(an MSDN article). In any case, unusual language features still are part of
the syntax. They must be parsed and they must be understood by programmers in
case they come across them. The ugly parts of a language don't go away just
because you don't use them that often.

Also, I'm not sure what you're objecting to. The throw clause? The ugly part
here is the overloading of the brackets and equal sign to specify the capture
list.

~~~
detrino
"[=]" Capture all variables by value, fair enough.

"mutable" Don't capture them const, this is very rare.

"throw()" Promise not to throw an exception, this is very rare for a lambda,
and also deprecated in favor of noexcept.

"-> int" Explicit return type, usually not needed, and nearly never needed in
C++14.

I was objecting to a contrived example used out of context to make C++'s
syntax look worse than it is.

~~~
rayiner
I'm not trying to make C++'s syntax "look bad." The standard itself makes
C++'s syntax look bad. All those features, except maybe the empty throw
clause, are basic things someone needs to know to use lambdas properly and to
read code that uses lambdas. We're not talking about deprecated, never-used,
features like "export" here.

I don't understand your line of reasoning here. Is syntax not ugly if you
usually don't use the ugly parts? I think everyone would agree that the parser
ambiguity that used to exist between "vector<vector<int>>" and the right-shift
operator ">>" was ugliness in the syntax. Is it a defense that you don't
usually instantiate a template with an instantiation of another template?

~~~
greyfade
None of these features, except the capture clause, are things that anyone has
to _learn_ to use C++ lambdas; they should know these other features anyway,
from other parts of the language.

The late return type syntax (`-> type`) works with nearly all functions (`auto
fn(foo) -> bar { ... }`), the `throw()` (now `noexcept`) specification has
been used for long time, and `mutable` is a well-known syntax for potentially
const objects; so the lambda syntax is, or at least _should_ be, unsurprising.

To say it's ugly is not saying much. We all know C++ is ugly. This is a given.
But singling out lambda syntax, which is comparatively easy to grasp and
largely consistent with the language, doesn't really make a case.

------
mrottenkolber
_I won 't go into details about deficiencies of Lisp. That's another problem
to be discussed; an extremely painful problem, in fact._

I don't buy it. Nobody wants to talk about these apparently obvious (to non
Lispers) problems of Lisp, yet they are still there? Bring them up and be
argued with or be quiet.

Later he talks about the ugliness of Lisp syntax, especially CL, but Scheme
and Clojure are supposed to be better. On what basis? I may admit scheme is
elegant, but calling clojure's syntax an improvement over CL is a pretty long
jump.

~~~
blueblob
I think many people that don't like lisp haven't spent much time with it. I
personally do not like lisp. I took an artificial intelligence class where we
wrote search techniques in common lisp. One problem that I had with lisp is
the end of my A* search had a massive number of parenthesis at the end. I
think this would have been a little better if we were taught about common lisp
structs earlier on. My next big problem was that I was coming from C where you
occasionally put extra parenthesis in to group portions of code (sometimes
necessary in macros or if statements). This is a very bad habit in lisp
because it tries to call a function.

~~~
dllthomas
There've been dialects of Lisp where a single ] would close all outstanding
open parentheses. Having never actually used one, I reserve judgment on
whether this is a good or bad idea.

~~~
AUmrysh
I think, just like any other syntactic sugar, it's fine if used with proper
understanding of possible consequences. I'd like to see lisp use indention
instead of parens like Python. I'm sure someone's done it and it isn't popular
for some reason or another, but I think getting rid of the parens would go a
long way to making people stop hating LISP.

~~~
blueblob
I don't see how lisp could get rid of parends with indentation. Parends are
used for function calls so you would then have to indent one extra level to
call a function? If there was a workaround for the function calls, then it
would also require every function to be written on a separate line, there
would be no concept of a one liner.

~~~
sparkie
Indeed. A workaround would be to introduce some infix operators (there are
various lisps that do so), so you could use something like '$' for function
application and end up with a syntax like Haskell.

Of course, this is a terrible idea and very un-lispy, because one of the key
benefits of lisp is that it's simple to parse. Introducing infix operators
destroys that simplicity.

Another workaround, which I quite like, is to simply remove all the "lisp
syntax" entirely, and edit a tree directly using a syntax directed editor.

------
dllthomas
_" In fact, many people claim that Lisp "has no syntax". This really means
that Lisp has uniform syntax: every piece of code is just a list delimited
with parentheses."_

For an appropriate definition of "uniform syntax", perhaps. What I mean when I
say "lisp has no syntax" is that you effectively write out the AST. If the
syntax were somehow uniform but had a radically different structure than the
AST I would claim that Lisp has syntax.

~~~
chimeracoder
> What I mean when I say "lisp has no syntax" is that you effectively write
> out the AST

Agreed. This is a very common misunderstanding of the statement, but
understanding the meaning of the statement correctly demonstrates the single
reason that Lisp is so powerful: its homoiconicity, and everything that
implies.

------
ufo
One thing I still miss from this and most other Lisp tutorials is good
practical examples of problems you would solve with macros. Its one of the
biggest differences between Lisp and other languages but I have no idea where
I should use macros and where I should simply use functions instead.

For example, in this tutorial he shows a stopwatch macro but this is precisely
the sort of stuff that is easy to do with lambdas:

    
    
      (define stopwatch (body)
        (start-timer)
        (body)
        (end-timer))
    
      (stopwatch (lambda () (my_code 42)))

~~~
zck
Sure, you can do it that way -- but isn't it ugly? Isn't it harder to read?

One thing I recently did that required a lot of macros was writing a unit test
framework for Arc, the Lisp Hacker News is written in. (framework, with
examples, here: [https://bitbucket.org/zck/unit-
test.arc](https://bitbucket.org/zck/unit-test.arc)) So to create a suite, you
write:

    
    
        (suite math
               this-will-pass (assert-same 4 (+ 2 2))
               this-will-fail (assert-same 3 (+ 2 2)))
    

Like stopwatch, instead of passing a lambda that runs the body of the unit
test, it just lets you put the body in. Why? In c-like languages, you don't
write an _if_ statement this way:

    
    
        if((val % 2 == 0), lambda (): { return true; }, lambda (): { return false; })
    

Isn't it easier to write:

    
    
        if(val %2 == 0) {
          return true;
        } else {
          return false;
        }
    

So if using lambdas isn't good for _if_ , why is it good for _stopwatch_?

Also, none of _math_ , _this-will-pass_ , or _this-will-fail_ are predefined
in the language, or even by my framework. You don't have to use strings, as
you might otherwise:

    
    
        (suite "math"
               "this-will-pass" (assert-same 4 (+ 2 2))
               "this-will-fail" (assert-same 3 (+ 2 2)))
    

Why might this be useful? Well, when you're writing a function:

    
    
        int abs(int val){ return val<0 ? -val : val; }
    

Wouldn't you find it annoying to write:

    
    
        int "abs" (int "val"){ return val<0 ? -val : val ; }
    

So Lisp lets you incorporate your code as part of the language, and doesn't
force you (as much) to hammer your thoughts into the built-in structures of
it.

~~~
ufo
> Sure, you can do it that way -- but isn't it ugly? Isn't it harder to read?

Its the old tradeoff between using long-winded existing constructs or paying a
complexity cost and forcing the reader to learn a new abstraction or DSL.

But I like your example. I imagine the extra lambda or string-quoting noise
starts getting significant when you have lots of tiny test cases.

~~~
zck
>Its the old tradeoff between using long-winded existing constructs or paying
a complexity cost and forcing the reader to learn a new abstraction or DSL.

Yeah, that's definitely true. When macros are used to add to the language,
you're acting, by definition, as a language designer. You can make good or bad
choices. I normally think it's good to eliminate boilerplate (e.g., the
_lambda_ calls in the examples) and to simplify things (passing bare symbols
instead of strings). To my eye, the new coder actually has to learn _less_
than if they had to remember to wrap with lambdas, or use strings: you code
just like other parts of the language. And that's a win.

However, bad choices with macros can definitely be made. Some people consider
the _loop_ macro ugly, because it's a hairy ball of special cases with new
syntax.

------
avifreedman
It's more likely to be GC that makes Lisp unattractive as an alternate to C++
for the things for which I'd normally use C/C++. I have spent way too much
time wrangling Wowza, Hadoop, and other things written in Java in the last
years, and have yet to see a good incremental garbage collector in a JVM to
support things that do gigabits of throughput and need to do it every second
day in+out. Pointers to the contrary are welcome...

------
spdegabrielle
Racket [[http://www.racket-lang.org](http://www.racket-lang.org)] is a modern
Lisp _with_ STATIC TYPING.

Quote from the static typing example on home page:

Racket's type system is designed to let you add types after you've worked for
a while in untyped mode — even if your untyped program wouldn't fit nicely in
a conventional type system.

#lang typed/racket

;; Using higher-order occurrence typing

(define-type SrN (U String Number))

(: tog ((Listof SrN) -> String))

(define (tog l)

    
    
      (apply string-append (filter string? l)))
    

(tog (list 5 "hello " 1/2 "world" (sqrt -1)))

To run the example, install Racket, start DrRacket, paste the example program
into the top area in DrRacket, and click the Run button. Alternatively, save
the program to a file and run racket on the file.

~~~
brudgers
Hell, you don't even need to use typed/racket to implement static types. You
can just use contracts with plain old Racket to achieve true
lispyness...because contracts let you impose static type-checking selectively
within your code and for staticly-typed code to symbiose with dynamically-
typed code.

Racket Guide on contracts: [http://docs.racket-
lang.org/guide/contracts.html](http://docs.racket-
lang.org/guide/contracts.html)

Racket Reference on contracts: [http://docs.racket-
lang.org/reference/contracts.html](http://docs.racket-
lang.org/reference/contracts.html)

There's even this groovy academic paper:
[http://www.eecs.northwestern.edu/~robby/pubs/papers/oopsla20...](http://www.eecs.northwestern.edu/~robby/pubs/papers/oopsla2012-stff.pdf)

It almost goes without saying that contracts are the mechanism by which
typed/racket is statically typed.

------
brudgers
Calling "strawman" on:

 _Which is more readable:_

The article misformats the example Lisp code [at least in two of my browsers]:

    
    
        (aset a x (+ (aref a x)
                          (aref b (- x 1))
                          (* 3 (aref b (+ x 1)))))
    

should be:

    
    
        (aset a x (+ (aref a x)
                     (aref b (- x 1))
                     (* 3 (aref b (+ x 1)))))

~~~
S4M
Yeah, I wanted to point it out as well, but I realized it's the text renderer
that didn't format his expression correctly. If you do a copy-paste, you'll
see that he indented it properly.

~~~
brudgers
I did in fact copy and paste the code directly from the article into my
comment above. The only reformatting I did was to add the same number of
spaces to the beginning of each line in order to have HN display it as code.

If I was being pedantic about the formatting, the error would be excusable.
But the code is presented as evidence that Lisp code is hard to read, so the
poor formatting is highly relevant. Furthermore, when presenting C++ code
earlier in the article, the formatting is correct:

    
    
        typedef std::map<std::string, int>::value_type vtype;
        vtype things[] = [vtype("one", 1),
                          vtype("two",   2),
                          vtype("three", 3)];
        std::map<std::string, int> m3(&things[0]

------
michaelfeathers
It would be interesting to write a steganographic C++ program which is a valid
lisp program when everything but identifiers and parentheses are stripped
away.

~~~
greyfade
That would be fairly easy if each line were prepended by a semicolon.

------
pbsd
This is mostly orthogonal to the article's point, but the motivating example
ignores the actual syntax that already exists in C++(11) to initialize
classes:

    
    
        std::map<std::string, int> m1 = 
        {
            {"one", 1}, 
            {"two", 2}, 
            {"three", 3}
        };
    

It may be relevant to mention IntelLib [1] here, which is one of many (ab)uses
of C++ to enact DSLs.

[1] [http://www.intelib.org/intro.html](http://www.intelib.org/intro.html)

------
cageface
Until I can write something like Ableton Live in _any_ Lisp I'll pass, thanks.

~~~
Jtsummers
I'm trying to figure out why you posted this, it just seems like a non
sequitur. Setting aside, for a moment, the issue of performance on signal
processing tasks, from a GUI perspective there's no reason that an interface
akin to Ableton Live shouldn't be producible in Common Lisp, Clojure or any
currently maintained Scheme implementation.

If you're dependent on 3rd party C/C++ libraries for the signal processing,
many schemes are pretty good for FFI, I've not used Clojure FFI or Common Lisp
FFI so I can't comment much on them. Clojure seems like it would be as good or
at least no worse than Java for interfacing with existing C/C++ libraries.
Common Lisp FFI is going to be implementation dependent (like with scheme). I
do know that Chicken Scheme's FFI is pretty easy. I made a wrapper for a
subset of GLUT and OpenGL, and adding additional components as they became
relevant to the project was straightforward.

Regarding performance, I won't comment on Common Lisp or Clojure, I never
measured their performance for numeric tasks. Scheme, however, with something
like Chicken Scheme has good performance, and again FFI is straightforward so
making a C module to do some heavy lifting is not unreasonable.

~~~
cageface
Clearly the signal processing/audio side of the app can't be written in Lisp,
and that actually makes up a pretty large chunk of the code.

And having to bridge everything over FFI really isn't very practical for a UI
of this complexity either for performance and code overhead reasons. Being
able to access your internal data structures directly without jumping through
FFI/translation hoops is a big win for both efficiency and code maintenance.

So Lisp zealots can ding my comment all they want but it doesn't change the
fact that intelligent, informed programmers all over the world continue to
reject Lisp for very practical reasons that the true believers of the Lisp
community will never understand.

