
C++11 and Boost - Succinct like Python - daivd
http://fendrich.se/blog/2012/10/31/c-plus-plus-11-and-boost-succinct-like-python/
======
viraptor
"you will notice that it is more or less as succinct as the Python code." ???
I hope that this person writes better python than this:

    
    
        const map<const string, const tuple<int, int, StrToStr>> TagDataMap {
          {"title" , make_tuple( 3, 30, stripnulls)},
          {"artist" , make_tuple( 33, 30, stripnulls)},
          ...
    

it will be succinct when the whole type can get reduced to "auto". Python
would just do:

    
    
        TagDataMap = {
            'title': (3, 30, stripnulls),
            'artist': (33, 30, stripnulls),
            ...
    

Ord implementation is also crazy:

    
    
        int i = static_cast<unsigned char>(s[0]);
        return (boost::format("%1%") % i).str(); 
    

considering it doesn't even handle different string encodings.

I don't want to complain about C++. It's a great language and has its uses.
But it's not succinct and it's far away from what scripting languages provide.
Claiming otherwise is close to fanboyism. Not being succinct is not a bad
thing. There are many other things that C++ does for you that Python doesn't.

~~~
fusiongyro
I think you're being unnecessarily critical. The article begins with "I think
that it is possible to write code in a style that is almost as painless as in
a modern dynamic language like Python. I also think that is not so well known
how much C++ has changed for the better, outside the C++-community. Hence this
post."

The difference between make_tuple(...) and (...) is not significant compared
to what it would have been before without list initializers at all and having
to manually populate the map. That's something people who knew C++ of the past
but not C++11 would be happy to see. The rest of the code is a great
demonstration of these new features as well. That's the point: to demonstrate
C++11, not undermine Python.

The article ends with some cogent criticism of C++. If this sets of your
fanboy detector, you should turn it down a little.

~~~
viraptor
But that's a completely different claim. I agree that C++11 is much nicer than
old C++. I really like the improvements. I'd be glad to see that argument in
his post instead.

That's not what he wrote though. The title is "C++11 and Boost - Succinct Like
Python", the contents say "almost as painless as in a modern dynamic language
like Python". That's what I can't agree with. There's still lots of supporting
syntax that doesn't actually do anything for the end result.

~~~
alpatters
The extra syntax is not there for nothing. It's adding type information. Thus
allowing error checking or dispatching by type or optimisations at compile
time. It is a cost in terms of syntax and readability but it's not for
nothing.

So for correct programs the end result might be the same in terms of values.
For buggy code and runtime speed that's not necessarily the case.

~~~
danking00
And this is where I decide the language is too far developed on the wrong
foundation.

I cannot put up with type systems that don't have complete or near-complete
type inference. I don't know why one would start a new project in a language
that didn't support Hindley-Milner.

~~~
Locke1689
Just to elaborate on what danking00 is saying, the extra syntax in this case
is not adding _any_ extra information (to the compiler). The left hand type of
the expression can be completely inferred at compile time, in this case. What
that required syntax is adding is pain, but no gain (except for imperceptibly
faster compile time).

In Haskell this would look like:

    
    
      TagDataMap = [
              ("title", ((3, 30, stripnulls)),
              ("artist", ((33, 30, stripnulls)),
              ...
    

Haskell will correctly infer that TagDataMap :: [(String, (Integer, Integer,
String -> String)].

 _Ok, technically this is an associative list, not a Python dictionary, but it
is a map and can be accessed like one. Hell, most people use dictionaries with
less than 10 items, which are much slower than arrays most of the time_

~~~
fusiongyro
"the extra syntax in this case is not adding any extra information (to the
compiler)." This actually isn't true in C++, because there could be many
classes in scope that could take a list initializer like this. In Haskell, no
literal with [] can "turn into" something besides a list, but that can happen
in C++:

    
    
        vector<int> items = {1,2,3,4};
        int[] items       = {1,2,3,4};
    

These have different types, so how would C++ know which one you meant if you
instead wrote:

    
    
        auto items = {1,2,3,4};
    

Again, in Haskell this isn't a problem because literals have essentially a
single type. (Edge cases around integers and strings notwithstanding).

 _Edit_ : Just to clarify, in a Hindley-Milner system you could maybe get away
with something like that, but everything you name in an HM system you must
use, and that isn't the case in C++:

    
    
        void foo() {
          auto items = {1,2,3,4};
          return;
        }
    

I can then make two classes with list constructors:

    
    
        struct FooClass {
          FooClass(std::initializer_list<int> list) {
            cout << "Made a Foo!" << endl;
          }
        };
    
        struct BarClass {
          BarClass(std::initializer_list<int> list) {
            format_your_hard_disk();
          }
        };
    

Obviously there are consequences to choosing the right type, but the type of
that value never leaks out of the function. Nevertheless, because side-effects
can happen anywhere, even in a constructor, C++ cannot optimize that out.

This might be a convoluted example, and it may be flawed, but conjuring up
others is not hard and demonstrates that C++ simply cannot ever have true HM
type inferencing. Since the "real deal" is not possible, the language is
complex and the standard is large, I would not expect to be able to live
without annotations in C++-land. (Again, the FQA makes the horror of multiple
non-orthogonal solutions to the same problems quite clear).

~~~
danking00
Your example,

    
    
        void foo() {
          auto items = {1,2,3,4};
          return;
        }
    

is the case of poorly designed syntax, in a truly Hindley-Milner system there
is _no_ expression which doesn't have a type. Now, we could add some syntax
that screws that up, say:

    
    
        let v = I-HAVE-AN-AMBIGUOUS-TYPE in 0
    

But that's rather silly, isn't it? If a value isn't used then I would,
personally, like my language to optimize it away. So why not ditch such silly
syntax?

This is part of why I said that the foundations of C++ are too far-gone.

And this syntax isn't necessary to save on typing. In a language that
supported syntactic macros (such as Scheme or Racket) we could write something
like:

    
    
       auto items = my-vector-syntax(1,2,3,4);
    

which expands to something like:

    
    
        auto items;
        items.push_back(1);
        items.push_back(2);
        items.push_back(3);
        items.push_back(4);
    

If you're interested in true syntactic macros for non-sexp languages (though I
do suggest getting over the parentheses, my color settings make them nearly
indistinguishable from the background) look at Rust [1].

Actually, Rust also has type inference [2].

Hell, stop programming in C++ and start using Rust! [3]

[1] <http://dl.rust-lang.org/doc/tutorial-macros.html> [2] <http://dl.rust-
lang.org/doc/0.4/rust.html#type-system> [3] <http://www.rust-lang.org/>

~~~
fusiongyro
Having programmed Haskell for the last seven years (and C++ for zero) I have a
fairly decent handle on what HM is all about. If all you had said is that the
foundations of C++ are too far gone, there wouldn't be anything to discuss.
But the foundations of C++ being what they are, there's no point being
offended when unfixable things go unfixed. I love HM, but you can't just throw
it in any old language simply because it's cool. The language's semantics need
to allow for it, and they just don't with C++.

~~~
danking00
Correct, I do not suggest adding HM to C++.

I suggest using a HM language when you start a new project.

------
Jabbles
Is it too late for me to bang my Go drum?

<http://play.golang.org/p/53jSv32wSF>

(You can't access the filesystem on the playground, so it doesn't run.)

* Look at that error handling. Mmmmhmmm, clear and explicit. If something goes wrong, I'll know about it.

* Apart from, of course, errors that I ignore, such as when converting the year from 4 chars to an int (line 54). I don't care if I can't parse that.

* Note the difference (lines 54, 56) between parsing the track (which is a byte), and the year, written as 4 digits. The byte can just be cast to an int, the string needs to be parsed by the strconv package.

* I have a little bit of defensive programming in the form of a panic(), which would tell me if something that I thought was impossible has happened.

* defer on line 19 makes sure the file closes if we managed to open it, regardless of when we return.

* type casts are explicit, even from int to int64 (line 20)

* I don't specify which interfaces a type implements, the compiler handles that all for me.

* Parse() returns map[string]interface{}, which allows me to store anything (in this case just ints and strings) in a key-value store.

* A type-safe printf! "%v" (line 67) uses reflection to look at the type of the argument and deduce the natural format. So I can pass it an int or a string and it works :)

* On line 86 I pass this weird new type to a printf function and it goes ahead and uses the String() method that we defined. If we hadn't defined that we'd get a printout of the type, which in this case would be the 128 bytes we read.

~~~
unoti
At the end after line 24 do we need another line of code that reads data into
the newly created byte array? If so, that says a lot about the readability of
the code since I'm not a Go developer...

~~~
Jabbles
I'm not sure what you mean. Could you be more specific?

Line 24 makes a byte slice, line 25 populates it with data read from the file.

~~~
unoti
Turns out, my iPad was truncating the whole thing at line 24. Sorry,
nevermind!

------
Kurtz79
It's succint, all right (well, sort of).

But Python's major strength is readability, even more than coinciseness, or
better, to provide both at the same time.

C was born as a terse language, sacrificing readability for coinciseness (the
original examples in K&R are incredibly succint, almost elegant, but far from
readable). I can't see many improvements in C++ (a language that arguably has
worse coinciseness than C, without gaining in readability in the process)in
that regards, even with the new version.

I work with C/C++ and I could easily understand a Python script even when I
wasn't that familiar with the language, the same cannot be really said for
that code in C++.

BTW, I'm not bashing C++, that has a lot going for it,just not readability and
coinciseness.

~~~
ajross
I call shenanigans. That's true of simple scripts, I'm sure. But you can't
seriously claim to me that you can understand decorator idioms, iterables or
list comprehensions without a deep understanding of the language. What you say
might have been true for Python c. 1998, it certainly isn't true today.

Broadly: reading code is just hard. It's much harder than writing code. There
are no non-trivial codebases that can be considered "easy to read" (if you
think there are, then you're fooling yourself), and the choice of language
makes only mild difference.

( _edit: I'm amused at the multiple people who had to jump in to explain
"decorators aren't hard!" instead of responding to my core point. First, I
know what a decorator is. Second, no one who isn't a reasonably proficient
python programmer is going to have any clue what @classmethod does or why all
the old code doesn't break without using it. It's non-standard, language-
specific syntax in exactly the same way that an STL iterator is, and the only
reason you think one is easy and not the other is because you know Python and
not C++._ )

~~~
Kurtz79
That is a given, I agree.

But a complex program written in C++ (or using advanced C++ constructs) will
scale much worse than a Python one, in my opinion.

~~~
netmare
I don't think that's necessarily true. In my opinion, both languages can be
used by novices for simple scripts and by experts for complicated tasks.
However, C++ seems to be getting easier for simple tasks (lambdas,
initializers, auto, alias templates, etc.), while Python is getting more
complex/powerful (metaclasses, exception tweaks, new I/O and function
annotations — just to name a few).

------
songgao
Well, I liked C++11 a lot. But now I think I'll replace it with Go whenever
possible. I don't really mind so much the verbose syntax. What matters is how
difficult it is to write efficient code.

I recently rewrote one of my C++ projects(which uses C++11 features and
Boost.asio) in Go. It took me half the time, less than 2/3 lines of code
compared to the C++ version. This is expected. But most surprisingly, despite
that I tried my best thinking about how to make it as efficient as possible in
every detail when writing C++ version, and that I'm quite new to Go, the Go
version still achieves nearly 100% higher throughput than C++ version.

Maybe my C++ skill sucks. But I guess I'll just let it suck.

~~~
daivd
Go is certainly a fine language. One common instance where I would prefer C++,
though, is when you are writing libraries for others to use. A language that
runs on a VM can not easily be used as a library inside a language that uses
another VM. You cannot easily write a library for Python in Go or vice versa.
If you use a VM-less language like C or C++, your work can be used (through
various wrappers, like SWIG or that Apache thing) by everyone.

~~~
ygra
Doesn't Go compile to machine code as well without a VM in between?

~~~
pyre
Go has garbage collection, which I think some people confuse with using a VM.

------
ctrlaltesc
What I find with C++ is that there are many languages inside it. Put 5 C++
programmers in a room and you'll probably end up with 6 different styles.

That the language may have some Python-esque tendencies (given very particular
language, compiler and library versions, and a depth of knowledge not required
in the Python equivalent) doesn't seem that interesting. Especially when you
consider that 9/10 C++ programmers you meet don't actually code in that style
and don't intend to.

------
huhtenberg

      cout << join(pm | transformed([](propMap::value_type pv){...
    

I'm sorry, but as "succinct" as it is, this is basically an unreadable
bullshit. Reminds me strongly of a macro abuse in C.

~~~
daivd
It is only unreadable bullshit the first time you read it. If you use ranges,
filtered and transformed for these kinds of tasks, it looks idiomatic.

~~~
morsch
Yep. It looked idiomatic to me -- and I don't even know C++11! I don't care
for the unqualified access to join and transformed, though, even if it is more
Pythonesque.

------
bcoates
This is neat and C++11 is pretty exciting, but one thing that C++ doesn't need
is the further propagation of tuples into non-generic code. Requiring
make_tuple instead of allowing shorthand was the right decision.

Tuples in python are a reasonable tradeoff between not wanting to declare
anything and the hassle of anonymous structure. This doesn't apply C++ where
the equivalent is the POD struct:

    
    
      //In the olden days we could not initialize a map inline like this
      //Key -> metadata mapping. Unfortunately strutcts cannot be declared
      //inside a template declaration.
      struct TagData { int start; int length; StrToStr mapfun; };
      const map<const string, const TagData> TagDataMap {
        {"title"   , { 3,   30, stripnulls}},
        {"artist"  , { 33,  30, stripnulls}},
        {"album"   , { 63,  30, stripnulls}},
        {"year"    , { 93,   4, stripnulls}},
        {"comment" , { 97,  29, stripnulls}},
        {"genre"   , {127,   1, ord}}};
    

Creating a named struct pays off when it's time to use the Map, no extra
locals or tie() needed to write clear code:

    
    
      //for loops over collections are finally convenient to use.
      for(auto td : TagDataMap){
        //C++ created a horrible precedent by making the data type of
        //a map pair<K,V> instead of struct ValueType { K key; V value; };
        auto tdd = td.second; 
        ret[td.first] = tdd.mapfun(sbuf.substr(tdd.start, tdd.length));
      }
    

[http://liveworkspace.org/code/bcd52515fb7161858e974b7ff3c0aa...](http://liveworkspace.org/code/bcd52515fb7161858e974b7ff3c0aac5)

~~~
daivd
Yes, of course a POD is better, but that would be cheating, since this is
supposed to show that you can do the Python stuff, including tuples and tuple
deconstruction, just like in the linked Python original.

Perhaps I should have mentioned that, though.

------
danieldk
This post shows that C++11 is actually quite a comfortable language.
Programming in C++98/03 was often a necessity for me (good for making
performant cross-platform software), while I disliked the language quite a
lot. The introduction of type inference, closures, range-based for loops, and
uniform intialization makes many things that used to be tedious much simpler.

That said, I guess we have to live with C++03 for many more years. E.g. in one
application that I co-developed, we use a library that can currently only be
compiled without a substantial amount of effort on Visual Studio 2005 or 2008.
The DLL compiled with older versions is not compatible with 2010 or 2012. So,
we are basically stuck in 2005 or 2008 land for now.

------
unwind
Great post, it's always fun to see the cutting edge of C++ revealed. It's such
a strange land. :)

To me, this post would have been 10X more interesting if there was some
elementary benchmarking included. If it's about the same speed as the
corresponding Python code, which I'd bet is easier to write for more
programmers, then I don't quite see the point being as clearly proven.

If it's 100 times faster (or whatever), then it gives more credibility to the
idea of writing code like this in C++ to begin with, and to learning all the
new language features that makes it safe while being that much faster than
Python.

~~~
daivd
I wrote this post.

The program is I/O-bound, so the only speed improvement comes from not having
to start up the Python interpreter.

If the task was CPU bound, you would get a great performance boost (sometimes
100x over CPython), but that is well known.

There can be other reasons than performance for writing in C++. Used well, the
strong static type system can catch many bugs. I suspect (but I cannot prove)
that it could be almost as good as Haskell.

I use Python for most things, though, so I don't really advocating switching
to C++ for every little scripting task.

~~~
pcwalton
It won't be as good as Haskell for memory safety. For example, this program,
using only C++11 idioms, crashes:

    
    
        #include <iostream>
        #include <vector>
    
        int main() {
            std::vector<std::string> v;
            v.push_back(std::string("Hello"));
            v.push_back(std::string("there"));
            for (auto ii = v.begin(), ie = v.end(); ii != ie; ++ii) {
                v.clear();
                std::cout << *ii << std::endl;
            }
            return 0;
        }
    

Preventing this sort of thing requires strong guarantees about aliasing (to
ensure that "v" can't alias the vector being iterated over), which the C++
type system can't help you with.

~~~
evincarofautumn
“C++11 idioms” include a preference for immutability, which leads to natural
factorings. You don’t have to worry about things like iterator invalidation
that way.

    
    
        #include <algorithm>
        #include <iostream>
        #include <iterator>
        #include <vector>
        using namespace std;
    
        template<class T>
        void print(const T& v) {
          copy(begin(v), end(v), ostream_iterator<string>(cout, "\n"));
        }
    
        int main(int argc, char** argv) {
          vector<string> v{"Hello", "there"};
          print(v);
        }
    

Also, Haskell is only so safe—at work we’ve taken to rejecting non-total
functions in review, because they cause more hassle than they’re worth. By
non-total, I refer more to “error” than non-termination, of course.

~~~
pcwalton
"const" doesn't help you in the presence of aliasing:

    
    
        #include <iostream>
        #include <vector>
    
        template<typename T,typename U>
        void print(const T& v, U& w) {
            for (auto ii = v.begin(), ie = v.end(); ii != ie; ++ii) {
                w.clear();
                std::cout << *ii << std::endl;
            }
        }
    
        int main() {
            std::vector<std::string> v;
            v.push_back(std::string("Hello"));
            v.push_back(std::string("there"));
            print(v, v);
            return 0;
        }
    

In general there are lots of ways to get non-const access to const data. You'd
need a sophisticated form of whole-program alias analysis to eliminate the
unsafety here. (Even if you didn't have the second "U& w" parameter, there are
other potential ways that "print" could get non-const access to "v"; global
variables, TLS, by accessing a member of "v", etc.)

~~~
ryanmolden
I don't disagree about aliasing, but a print function shouldn't be taking ref
to a non-const anything anyways, it should have no need to mutate the vector.
You can't clear through a ref to const. Your general point holds but I would
say I have probably written/found very few aliasing bugs in C++ over the
years, it just doesn't seem to arise often, perhaps your experience differs.
Alas there are few ways to prevent dedicated folks from writing really ill-
advised code.

------
mitchi
No thanks. I'll use D if I want a ton of features for free. This is not
beautiful code. I could work around it I guess because I've seen much worse
coming from C++ but it's really not my ideal of C++. But this is a small
example, the source code of the STL offers much more madness. Not to mention
the compiler messages you get for using templates...

------
talloaktrees
I know c and python, but not c++. While reading the code section, i totally
thought this was a wonderful satire of c++

~~~
daivd
But that is sort of the point. You should not write C++ like C. If a
C-programmer thinks your code is nice and readable, you are not using C++. The
C-stuff was just added to lure C-programmers over.

------
arctangent
Equivalent code written in D would look much more like the Python code than
this.

------
SiVal
Periodically throwing some new ingredients into old soup to freshen it up can
only extend the life of leftovers so far. There comes a time when, even with a
handful of fresh veggies, it's no longer good soup. You need to toss out the
leftovers, scrub the kettle, and start a fresh batch.

You will still need C++ for dealing with all the legacy C++ out there, but if
you are starting a new project, you ought to be able to get the small, fast,
efficient runtime advantages of a legacy monster like C++ from a much smaller,
simpler language with a modern standard library.

Maybe it will be Go, but even if not, we need a simple, safe, productive
language with modern features pre-installed (unicode strings, safe arrays,
lists, maps) and a modern standard library that statically compiles to small,
fast, native executables.

C++ with its "if you use the most recent 10% and pretend the old 90% doesn't
exist, it's a great language" ethos is not what we need for new code.

~~~
sodiumphosphate
I keep wishing for this magical language to manifest, like a C++ compatible
Boo. I only wish I were capable of building one myself.

------
simmons
I've been using both C++11 and Python lately. While the new C++11 features add
a lot of value, I still find it frustratingly verbose for some things. For
example, finding an element in a collection:

    
    
        std::string type = "foo";
        auto it = std::find_if(
            channel_widgets.begin(),
            channel_widgets.end(),
            [type](const std::shared_ptr<Widget> &w){
                return (w->getType() == type);
            }
        );
        if (it == channel_widgets.end()) {
            std::cerr << "widget not found" << std::endl;
        } else {
            std::shared_ptr<Widget> target_widget = *it;
            std::cout << "widget found." << std::endl;
        }
    

versus (for example):

    
    
        type = "foo"
        matches = [x for x in widgets if x.get_type() == type];
        if matches:
            target_widget = matches[0]
            print "widget found:",target_widget
        else:
            print "widget not found"
    

I may be missing out on some C++11 feature for doing this better. (Or even
some Python feature for doing this better!)

~~~
reinhardt
Can't comment on C++11 but the Python version is better written as:

    
    
        type = "foo"
        try:
            target_widget = (x for x in widgets if x.get_type() == type).next()
            print "widget found:",target_widget
        except StopIteration:
            print "widget not found"

~~~
kzrdude
You can also use the `next(x for x in ...)` form where you can even supply a
default value.

------
pif
The only point that finds me in disagreement is the need for a code standard
_that decides well on which parts should be used and how_. I don't understand:
why limiting ourselves? Why choosing such a well-tested instrument and
choosing NOT to use some of it?

~~~
bcoates
Some parts exist either for backwards compatibility with existing code or to
serve very narrow use-cases and should be used sparingly or not at all in new
development.

There's also a ton of redundant libraries and language features, resulting in
a lot of decisions being arbitrary choices between equivalents. Having the
same decision made throughout the code makes everyone who has to read the
code's life easier.

------
j_baker
I think people who jump on the "C++11 is as good as
Python/Ruby/Javascript/other-language-of-the-week" bandwagon are missing the
point, and that is that limitations are a good thing. _Can_ C++ be made as
concise and easily-readable as one of the above languages? I wouldn't doubt
it.

C++ can be made to do _lots_ of things, and it doesn't achieve that by being
elegant. It achieves that by trying to do everything possible under the sun.
Because of that, C++ is and always will be much more complex than the other
languages.

~~~
daivd
Is there really such a bandwagon?

~~~
pmr_
Yes, it apparently has. The largest problem I see in this new trend is
portability. Certainly, if you have full control over the environment and
require everything to be bleeding-edge you are fine. If not... Well, just have
a look at boost source code and you will see how concise nested workarounds in
#ifdefs really are.

If your environment is solely your own and you don't care about having your
users build something, fine.

------
activepeanut
Is it possible to build Boost for iOS with c++11 enabled in llvm-clang?

~~~
gilgoomesh
On iOS 5 and newer (required for libc++), using clang 3.0 and newer (required
for decent c++11 support), yes.

~~~
activepeanut
Can you explain how, or point me to a resource describing how to do it?

------
bitcracker
In other words: C++11 boosts Python :-)

I think only a hardcore C++ developer would claim that the author's sample is
"succinct". Honestly, C++11 is still far behind the easiness of Python (or
Scheme), even with Boost.

Funny, a decade ago Ada 95 (the "military" language for high-critical
applications) looked like a monstrous over-designed beast when compared to
C++. Today Ada 2012 looks elegant and even "small" when compared to C++11. How
times have changed :-)

------
malkia
And here goes your productivity - the real killer application for your link
times.

------
nimrody
I work with C++ daily and it still has many rough edges:

* Horrible error messages

* Even simple programs take ages to compile due to massive header files.

LLVM/Clang help on both fronts but it's still quite difficult.

D2 seems much more promising if you can do without the libraries.

------
jamesaguilar
And then you do an accidental implicit conversion from the iterator type
(const pair<...>) to the function type (pair<...>), return a reference to one
of the fields and BOOM, everything explodes.

------
ifij775
The changes to C++ are too little, too late. These changes should have been
made years ago, and C++ has lost momentum and credibility. Is anyone comparing
Python to c++? nope, only the other way around.

~~~
mattgreenrocks
Too little, too late? There's no zero sum game here. Perhaps you're conflating
Internet-popular (which doesn't mean shit) with useful?

~~~
daivd
Internet popular means libraries on github and good tutorials, means useful.

~~~
LnxPrgr3
Are you suggesting C++ lacks available libraries? Because for all of its
faults, lack of libraries is not one I've run into.

Many desktop and server applications are still written in C or C++, including
the Web browser I'm writing this on. Reports of their deaths are
exaggerations.

------
muyuu
I think this comparison is completely misplaced. A language like C++11 simply
doesn't even aim at being succinct like Python.

------
abyx
Succinct - I don't think that word means what you think it means

~~~
abyx
Also, s/Succinct/Python/

------
cjdrake
Nice try, Bjarne. I'll stick with Python :).

------
drivebyacct2
I hate to do it (who am I kidding, no I don't) but the battle in this thread
for static typing, succinctness and an EASY language that is easy to wrap your
head around... if those are things that make you happy to program in a
language, please give Go a shot. Here, take 10 minutes:
<http://tour.golang.org>

------
lbolla
My eyes just exploded! ;-)

------
16s
I find C++ much easier to read than Python. Here's how to reverse a string in
C++:

 __ _string s = "string";_ __

 __ _reverse(s.begin(), s.end());_ __

And here's how to reverse a string in Python:

 __ _s = "string"_ __

 __ _print s[::-1]_ __

In my opinion, the C++ version is far easier to read and just makes more
sense. Edit - This is just one small example, however, I find it holds true
for the entire languages in general. Also, I do a lot of Python and C++
systems programming so I use both frequently and while I prefer C++, I think
Python is the best scripting language available today.

~~~
BoppreH
Or you could use the "reversed" built-in function:

    
    
      s = "string"
      reversed(s)
    

It also has the plus of creating a generator, saving memory from a new array
allocation.

~~~
wting
`reversed(s)` returns an iterator. The much slower equivalent of `s[::-1]` is
`''.join(reversed(s))`.

It's odd how Python has list.reverse() but not str.reverse(). Probably a
conscious design decision sometime ago.

~~~
ot
list is mutable while str is not. So reverse() should return a new copy, but
it would duplicate the functionality of s[::-1] (which is quite idiomatic,
although a bit obscure)

