3 ms·
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
by Chris_Newton 11y ago
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.
- anon4 11y agoAs a C++ programmer, I would absolutely never write that code using STL's bullshit. The for loop is * shorter * easier to write * faster to compile * way faster to execute in a debug build
- simoncion 11y ago> * way faster to execute in a debug build Frankly, this doesn't _really_ matter. IIRC, most non-pathological use of the STL compiles into very reasonable code when run through an optimizer. Let's be real. If you're shipping non-optimized code to a client, you don't actually care about performance. :)
- dllthomas 11y agoThe "awkward" Haskell line could be rewritten: mapM_ print ys
- Chris_Newton 11y agoYes, that would have been a better choice. Thanks.
- dllthomas 11y agoUsing 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 11y agoI 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. :-)