What if C++ looked more like Python or CoffeeScript?

 

I’m quite fond of languages with minimal syntax. Not only it is easier to read and write code in these languages – it also provides an opportunity to reduce errors (both at compile time and at run time), when you consider that every character in a program has the potential to cause an error due to being misread or misplaced. In addition, long, dense lines of code littered with punctuation increase the cognitive burden on the programmer.

In this post, I would like to sketch out an idea for a modified C++ syntax with reduced noise. I don’t make claims about the practicality of implementing such syntax, and it’s by no means a complete solution. However, I thought that it’s an interesting thought experiment, and I’d enjoy a simpler syntax if it was available.

Another thing I should mention is that it isn’t meant to be a backward-compatible syntax change which could be incorporated into, say, C++17. What I’m exploring is a dialect of C++ with Python-like syntax but with most of C++ constructs and semantics intact.

The basic idea is very straightforward, and of course already applied in other languages. Remove semicolons and curly braces used to denote code blocks, make parentheses in control structures optional, make indentation significant (as in Python and CoffeeScript) – voila, less noise.

The code could look like this:

#include <iostream>
using namespace std
 
namespace utils
    template<typename T>
    auto square(const T& n)
        return n * n
 
int main()
    auto input = 0
    auto message = "Please enter a positive number"
 
    cout << message << ":" << endl
    cin >> input
 
    if input > 0
        cout << "Result: " << utils::square(10) << endl
    else
        cout << message << endl
    return 0

Generally speaking, the newline becomes the end of a statement, and indentation indicates a code block. Curly braces can still be used in initialization constructs.

Some statements (like template declarations) will still require multiple lines. This kind of syntax requires some constraints to be placed on how statements can be split across multiple lines, but I think these can align pretty well with existing good code style.

The rest of this post is about dealing with various details, e.g. where this syntax can be ambiguous.

Newline and semicolon as statement separators

Generally, end of the line means the end of a statement:

Point<int> x
Point<double> y
Point<double> pt = x + y

The semicolon can be repurposed to combine multiple statements on one line:

Point<int> x; Point<double> y
Point<double> pt = x + y

The semicolon continues to be used in the for loop, so it’s mostly unchanged:

sregex_token_iterator token_it(begin(input), end(input), regex, 1)
 
for (auto it = token_it; it != sregex_token_iterator(); ++it) 
    cout << *it << endl

Multiline statements

Some statements can’t practically fit on one line and need to be recognized as multiline. One example is templates:

template<typename T> 
struct Point
    T x, y
 
template<typename T>
auto square(const T& n)
    return n * n

The template keyword and type list must be followed by a class or function declaration on the subsequent line.

What about a function declaration which requires more than one line? For example the return type might need to go on a separate line:

template<typename T1, typename T2>
auto operator+ (const Point<T1>& lhs, const Point<T2>& rhs) ->
    Point<decltype(lhs.x + rhs.x)> 
{
    // ... implementation ...
}

This can be parsed by introducing a set of non-terminating symbols, i.e. symbols which can’t end a statement. The arrow can be one of those.

Sometimes it will also be necessary for function parameters or arguments to span multiple lines:

template<typename T1, typename T2>
auto operator+ (
    const Point<T1>& lhs, 
    const Point<T2>& rhs) ->
    Point<decltype(lhs.x + rhs.x)> 
 
    // ... implementation ...

This could be parsed in two ways: one is that the parser keeps concatenating lines until it finds the closing parenthesis; the other is that commas become part of the non-terminating symbol set, and the parser concatenates the next line as long as the current line ends in a comma. Arguments passed when calling a function can be treated in the same way.

Finally, what about multiline string literals? For example:

    // log entry regex
    R"(([A-Z][A-Z])(\d+) "  // Flight no. "ETA "
    "(\d+:\d+) "            // ETA
    "Actual "
    "(\d+:\d+))"            // Actual time

I think it should be OK to concatenate them just like it’s currently done in C++. Even though this could be interpreted as separate literals on subsequent lines, such an interpretation wouldn’t be of much use.

Optional parentheses in control structures

The parentheses in control structures are optional:

if input > 0
    cout << "Result: " << utils::square(10) << endl
else
    cout << message << endl
 
switch color 
    case WHITE: cout << "#fff"; break
    case GRAY: cout << "#c0c0c0"; break
    case BLACK: cout << "#000"; break
    default: throw runtime_error("Unrecognized color value")
 
for auto& elem : elements
    elem = square(elem)
 
try
    Autopilot autopilot
    auto engage_task = async(launch::async, &Autopilot::engage, autopilot)
    // ...
    engage_task.get();  // throws an exception 
catch const runtime_error& e
    cout << e.what() << endl
 
auto i = 0
do
    cout << "*"
    cout << "-"
while i++ < 10

The handling of the while in do-while is similar to how this is handled in normal C++:

do i++;
while (i < 10);

Sometimes the expressions have to be quite long:

while diagnostics_task.wait_for(seconds(0)) == future_status::timeout ||
    autopilot_task.wait_for(seconds(0)) == future_status::timeout ||
    controls_task.wait_for(seconds(0)) == future_status::timeout
 
    cout << "."
    this_thread::sleep_for(milliseconds(500))
 
cout << "System ready." << endl

There are two ways of handling this situation. The set of non-terminating symbols includes the operators, so lines will be concatenated as long as they end in operators.

Alternatively, the condition can be enclosed in parentheses, which provides the option of breaking it up freely, just like in normal C++:

while (diagnostics_task.wait_for(seconds(0)) == future_status::timeout 
      || autopilot_task.wait_for(seconds(0)) == future_status::timeout 
      || controls_task.wait_for(seconds(0))  == future_status::timeout)
 
    cout << "."
    this_thread::sleep_for(milliseconds(500))

Declarations vs definitions

Suppose we have a function declaration and a definition:

auto f(int n)
{}
 
auto g(int n);

After removing semicolons and braces they become ambiguous:

auto f(int n)    // declaration or definition?
 
auto g(int n)

A backslash can be used to distinguish a function with an empty body:

auto f(int n) \     // definition
 
auto g(int n)       // declaration

The only requirement is to have a blank line following the backslash as that will be considered the function body. One problem with the backslash is that using it this way conflicts with the currently existing translation rules, so perhaps another symbol is necessary. But in any case, the point is that I need some way of specifying an empty block instead of {}.

The same principle applies to classes:

class A \           // empty class
 
class B             // class declaration

Method access specifiers

There are two options of formatting access specifiers. The obvious one is to indent them to the same level as the rest of the class body:

class JetPlane : public Plane
 
    vector<Engine> _engines
 
    public:
 
    JetPlane(const string& model = "") : Plane(model) \
 
    auto require_service(const date_t& date) \
 
    protected:
 
    auto load_person(const Person& person) 
        _passengers.push_back(person)

However, the prevalent existing style appears to be to align access specifiers with the class specifiers, so this could be allowed a special case instead:

class JetPlane : public Plane
    vector<Engine> _engines
 
public:    
    JetPlane(const string& model = "") : Plane(model) \
 
    auto require_service(const date_t& date) \
 
protected:
    auto load_person(const Person& person) 
        _passengers.push_back(person)

Lambdas

A lot of the syntax can be translated in a straightforward manner. However, lambda expressions are one of the trickier cases. Let’s take an example:

template<typename Cont>
auto print(const Cont& cont)
    for_each(begin(cont), end(cont), 
        [] (auto elem) cout << elem << endl)

I’ve removed the braces around the body of the lambda given to for_each. There was actually a proposal to allow lambda body to be an expression which suggested introducing this kind of syntax into C++14, so this is possible to parse – subject to some restrictions.

However, lambda expressions can be a lot more complex than this, with multiple statements in the body and so on.

In a more complex scenario, lambda bodies could be demarcated via indentation:

// update the screens in an airplane cabin
for_each(cabins.begin(), cabins.end(), [=](auto& cabin) 
    for_each(cabin.seats().begin(), cabin.seats().end(),
        [=](auto& seat_screen)
            seat_screen.set_mode(mode)
            seat_screen.refresh()
    )
)

While we’re at it, here is another potentially problematic situation: a function taking two callbacks as arguments. I could pass two lambdas to it:

make_request([], [])

Note that an empty lambda is now reduced to the introducer. What if I want something more elaborate? For example:

make_request([] 
    cout << "Request successful" << endl
    , 
    [] 
        cout << "Request failed" << endl
)

If I try to use indentation for lambda bodies, it looks pretty ugly and confusing. Instead, it’s better to allow an unindented leading comma (this is the CoffeeScript solution):

make_request([] 
    cout << "Request successful" << endl
, [] 
    cout << "Request failed" << endl
)

What about a lambda which is defined and called immediately?

auto a = 10;
const auto const_val = [&] { return a; }();

After removing semicolons and braces, I get this:

auto a = A(10)
const auto const_val = [&] return a ()  // potentially ambiguous

This is potentially ambiguous, so the lambda needs to be wrapped in parentheses:

auto a = A(10)
const auto const_val = ([&] return a)()  // OK, the function call operator
                                         // is applied to the lambda

Standalone code blocks

Standalone code blocks are occasionally useful for scoping. But without curly braces, code blocks like this would merge into one:

auto test_log()
{
    {
        std::ifstream ifs(log_file_name_rotated.c_str());
        ASSERT_TRUE(stream.is_open());
    }
 
    {
        std::ifstream stream(log_file_name.c_str());
        ASSERT_TRUE(stream.is_open());
    }
}

Relying on indentation isn’t going to be enough here, I need to tell the parser that a new block started. A backslash on its own line can be used for that (with the same caveat as before – another character may be better):

auto test_log()
    \
        std::ifstream ifs(log_file_name_rotated.c_str())
        ASSERT_TRUE(stream.is_open())
 
    \
        std::ifstream stream(log_file_name.c_str())
        ASSERT_TRUE(stream.is_open())

Things I don’t know how to handle

One thing I don’t know how to handle is unnamed types:

struct
{
    int x;
    void print() const
    {
        cout << x; 
    }
} a;

I don’t see an easy way of delimiting the type definition from the variable without adding a new keyword or a symbol to mark an anonymous type. Personally, I’d be happy to live without anonymous types in exchange for the other benefits, but this may not be a good tradeoff for other people.

Syntactic constructs that wouldn’t work in C++

There are a few other constructs which are useful in other languages, so I considered them briefly, but decided they wouldn’t work or wouldn’t add much value in the C++ context.

Treating everything as an expression and optional returns

Another thing CoffeeScript and Ruby do is treating everything as an expression:

message = if lander_count > 10
    "Launching probe"
else
    "Waiting for probes to return"
end

Consequently, methods return the result of the last expression, and the return keyword is optional:

def can_land?
    landers.count > 0  # returns the result of evaluating expression
end

However, I think this kind of change would be too drastic, and it has performance implications which make it undesirable in C++.

Array comprehensions

CoffeeScript’s for is a mechanism for array comprehensions rather than a simple loop:

foods = ['broccoli', 'spinach', 'chocolate']
eat(food) for food in foods when food != 'chocolate'

While this is nice, I don’t think this would add that much to the range-based for syntax and functional style iteration already available in C++.

No distinction between definition and assignment

CoffeeScript doesn’t make a distinction between variable definition and assignment, so that a = 10. It makes the syntax simpler, but it also makes shadowing impossible:

a = 10  # new variable
func = -> 
    a = 20  # assigns to the same a as above

This is a deliberate choice on the part of CoffeeScript’s creator. I think it’s dubious even in CoffeeScript, and would be worse in C++.

A longer example

Finally, here is a longer example of the new syntax and the normal C++ syntax side by side. This is based on some code I borrowed from the MongoDB project. In addition to using the new syntax on the left side, I made a few C++11/14 style changes.

/*    Copyright 2013 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
 
#pragma once
 
#include <algorithm>
#include <cstdlib>
 
#include "mongo/base/status.h"
#include "mongo/logger/message_log_domain.h"
 
/*
 * Implementation of LogDomain<E>.  Include this in cpp files to instantiate new 
 * LogDomain types. See message_log_domain.h, e.g.
 */
 
namespace mongo 
    namespace logger 
 
        template <typename E>
        LogDomain<E>::LogDomain() :
            _minimumLoggedSeverity(LogSeverity::Log()), _abortOnFailure(false) \
 
 
        template <typename E>
        LogDomain<E>::~LogDomain() 
            clearAppenders()
 
 
        template <typename E>
        auto LogDomain<E>::append(const E& event) 
            for const auto& appender : _appenders
                if appender 
                    Status status = appender->append(event)
                    if !status.isOK()
                        if _abortOnFailure
                            ::abort()
                        return status
 
            return Status::OK()
 
 
        template <typename E>
        auto LogDomain<E>::attachAppender(
            typename LogDomain<E>::AppenderAutoPtr appender) 
 
            auto iter = std::find(_appenders.begin(), _appenders.end(), nullptr)
 
            if iter == _appenders.end()
                _appenders.push_back(appender.release())
                return AppenderHandle(_appenders.size() - 1)
            else 
                *iter = appender.release()
                return AppenderHandle(iter - _appenders.begin())
 
 
        template <typename E>
        auto LogDomain<E>::detachAppender(
            typename LogDomain<E>::AppenderHandle handle) 
 
            auto& appender = _appenders.at(handle._index)
            AppenderAutoPtr result(appender)
            appender = nullptr
            return result
 
 
        template <typename E>
        auto LogDomain<E>::clearAppenders() 
            for const auto& appender : _appenders
                delete appender
            _appenders.clear()
/*    Copyright 2013 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
 
#pragma once
 
#include <algorithm>
#include <cstdlib>
 
#include "mongo/base/status.h"
#include "mongo/logger/message_log_domain.h"
 
/*
 * Implementation of LogDomain<E>.  Include this in cpp files to instantiate new 
 * LogDomain types. See message_log_domain.h, e.g.
 */
 
namespace mongo {
namespace logger {
 
    template <typename E>
        LogDomain<E>::LogDomain() 
        : _minimumLoggedSeverity(LogSeverity::Log()), _abortOnFailure(false) 
    {}
 
    template <typename E>
    LogDomain<E>::~LogDomain() {
        clearAppenders();
    }
 
    template <typename E>
    Status LogDomain<E>::append(const E& event) {
        for (typename AppenderVector::const_iterator iter = _appenders.begin();
             iter != _appenders.end(); ++iter) {
 
            if (*iter) {
                Status status = (*iter)->append(event);
                if (!status.isOK()) {
                    if (_abortOnFailure) {
                        ::abort();
                    }
                    return status;
                }
            }
        }
        return Status::OK();
    }
 
    template <typename E>
    typename LogDomain<E>::AppenderHandle LogDomain<E>::attachAppender(
            typename LogDomain<E>::AppenderAutoPtr appender) {
 
        typename AppenderVector::iterator iter = std::find(
                _appenders.begin(), _appenders.end(),
                static_cast<EventAppender*>(NULL));
 
        if (iter == _appenders.end()) {
            _appenders.push_back(appender.release());
            return AppenderHandle(_appenders.size() - 1);
        }
        else {
            *iter = appender.release();
            return AppenderHandle(iter - _appenders.begin());
        }
    }
 
    template <typename E>
    typename LogDomain<E>::AppenderAutoPtr LogDomain<E>::detachAppender(
            typename LogDomain<E>::AppenderHandle handle) {
 
        EventAppender*& appender = _appenders.at(handle._index);
        AppenderAutoPtr result(appender);
        appender = NULL;
        return result;
    }
 
    template <typename E>
    void LogDomain<E>::clearAppenders() {
        for(typename AppenderVector::const_iterator iter = _appenders.begin();
            iter != _appenders.end(); ++iter) {
 
            delete *iter;
        }
 
        _appenders.clear();
    }
 
}  // namespace logger
}  // namespace mongo

I think the code on the left is much easier to understand thanks to reduced clutter. It also means that bugs would be easier to spot, and it would be faster to scan through, e.g. when looking for a particular function.

Another thing to note is that the new syntax results in substantial savings in terms of line count. If I don’t include the large comment at the top, the normal C++ code is 25% longer than my syntax. That’s despite the fact that it uses Java-style formatting for curly braces. If opening curly braces were on their own lines, the difference would be even more significant.

I believe this vertical compression is valuable, because it can make a difference between seeing a whole function or only part of it, a whole algorithm or only a portion. Seeing the whole thing at once makes it easier to understand it and reason about it.

The end

Are there other problems with parsing this syntax which I haven’t thought of? This is C++, so I’m sure there are! Even without trying to make significant changes to the syntax, C++ parsing is a complicated affair. Plus of course, C++ was never designed with significant whitespace in mind.

My intent was to see if this kind of change would be at all plausible, and what it would make the code look like. It seems that it could work, but it would likely require constraints to be placed on some of the things which can be written in regular C++.

If you spotted a particular problem, feel free to point it out in the comments.

 

Keen on mastering C++11/14? I’ve written a book about the C++11/14 features. You can get a VS2013, Clang or a GCC edition.

 

37 Responses to “What if C++ looked more like Python or CoffeeScript?”

  1. I’m a believer in making syntax more verbose if it means making it less ambiguous for the reader. Not convinced that removing semicolons or braces will have a beneficial effect.

     
    • It works in other languages though, so why not in C++?

       
      • Alex Korban
      • It doesn’t work in other languages.

        Semicolon insertion doesn’t work (Javascript)

        Meaningful whitespace is broken by tabs vs spaces (python)

        Making parentheses optional won’t work because C++ allows expressions that look too much like statements. For example…

        if (int x =5) { print(x); }
        turns into the strange looking
        if int x = 5 print(x);

         
        • I’m not suggesting semicolon insertion. Tabs vs spaces always gets brought up but it’s a non-issue in practice.

          Your example should be written as

          if int x = 5; print(x)

          or

          if int x = 5
              print(x)

           
          • Alex Korban
          • A major concern of mine is whether it is still feasible to parse C++ easily with these simplifications.

             
          • “easily” is used in a relative way here, since parsing C++ is already very difficult.

             
            • Yes, I share your concern in that regard.

               
              • Alex Korban
  2. 1) “Remove semicolons and curly braces used to denote code blocks”
    2) “make parentheses in control structures optional”
    3) “make indentation significant”

    I strongly and wholeheartedly disagree with 1 & 2, I believe it makes the code less readable. But I would enjoy having number 3, make correct indentation mandatory !

    I guess it comes down to personal taste. C/C++ community is used to read code that is organized by semicolons, curly braces, and parentheses; removing them will only cause unimaginable confusing, and it can break backward-compatibility.

    Countless times I have used new line to organize my code (e.g. functions with more than 2 parameters, lines of code consisting of multiple arithmetic operations, say cross product). If you remove “;” then you are forcing me to write everything in a single line which will actually hurt readability!

     
    • Alan
    • Hi Alan. Of course this would break backward compatibility! It’s pretty much a different language.

      Naturally, it also looks a bit alien. But I was interested in seeing whether I could combine a Python-like syntax with C++-specific constructs and semantics.

      Regarding long statements, I addressed some of these issues in the post – e.g. you could easily write one function parameter per line, and template declarations pretty much require multiple lines. I realise C++ statements can be quite long, so there needs to be a way to break them into multiple lines.

       
      • Alex Korban
  3. can’t have C++ without braces dude.

     
    • gubatron
    • I know, it’s like the sacred cow, right? :)

       
      • Alex Korban
  4. I already do that. MyDef is essentially a macro system with semantic emphases. Right now it has output modules for C, Perl, and PHP. I write all my C and Perl programs in MyDef exclusively now. Let me know if you are interested.

     
    • C++ is trickier than C, e.g. because of templates and lambdas. But I think it’s still workable (hence this post). Do you have your system online somewhere?

       
      • Alex Korban
  5. Braces are totally replaceable with indentations. Semicolons are redundant when we enforce one statement one line rule (all modern editors wraps line for me, so I never felt such style restrictive).

     
  6. Actually the only occasion I felt long lines uneasy is when used in if statement with dozens of logical combinations. I work around this by building up the condition macro in succession lines.

     
    • I think it’s possible to have unambiguous line breaks in a statement, as long as there are some restrictions on how you break it up across lines.

       
      • Alex Korban
      • Of course it is possible, but not necessarily beneficial. The whole point is to have semantically readable code; long statement with multiple semantic layers are difficult to read, and should be discouraged.

         
  7. Here’s another (rather old) alternative to C++ syntax. Your proposal is to change the lexical syntax, while these people want to leave that alone and change the rest.

    http://www.csse.monash.edu.au/~damian/papers/HTML/ModestProposal.html#section1

     
    • Mark
    • Interesting, thanks – it reminded me of Pascal. I like their ideas, however it’s a very different direction. One of my goals was to keep code recognisable as C++ – just with less noise. Their focus seems to be primarily on making declarations/definitions easy to parse for a reader.

       
      • Alex Korban
  8. This doesn’t seem to allow single line if or while statements, because parentheses are optional.

    I like single line if when it is appropriate. (Python allows single line if by using colons.) Apart from my taste, C preprocessor macros can’t expand to more than a single line, so this seems to prevent macros expanding to contain control structures. Is there an easy fix?

     
    • Seo Sanghyeon
    • To duplicate my responses to your comments on HN:

      You could do it by separating the body with a semicolon:

      if something_is_true(); log(“warning!”)

      I haven’t really considered the preprocessor as I prefer to avoid it as much as possible.

      To resolve the ambiguity in something like “if a; if b; c; d”, I’d make it parse as “if (a) { if (b) { c; d; } }” and leave it at that. One of the reasons for significant indentation is to discourage this kind of code layout because it’s hard to read.

       
      • Alex Korban
  9. I quite like the idea! Do you have something to play with online somewhere?

     
    • Dan
    • Nope, this is purely a discussion at this point. I haven’t addressed some things like preprocessor use either.

       
      • Alex Korban
  10. If you think that removing curly braces and semi-colons is the actual problem of C++ syntax, you obviously have no idea what you are talking about.

    I was expecting something a lot more in-depth than this.

    Please stop writing articles about the subject.

    Oh, and you seem to be selling a book about C++ too? dear lord.

     
    • yoanizer
    • Wow! Just wow! Relax kid! What an utterly useless comment. Is having a discussion about a tool we use everyday forbidden my lord? We didn’t get the memo! Sorry your royal highness! Kids pack up your things the discussion is over, lord yoanizer has spoken!

       
      • Alan
  11. I like this a lot! I don’t agree with all the proposed syntax of course but I like the idea.

    I would also do this for variable declarations :-

    int x;
    Becomes
    var x : int;

    That is add a var keyword to introduce a variable declaration, and have the type after a : Then you can also write

    var x
    Which means “auto x” now.

    Just seems cleaner to me :)

     
    • John
    • Yes, that does make sense but it makes the language less recognisable. You might be interested in this proposal somebody else mentioned:

      http://www.csse.monash.edu.au/~damian/papers/HTML/ModestProposal.html#section1

      They had a lot of ideas about the declaration/definition syntax.

       
      • Alex Korban
    • Would you please elaborate what use this change might possibly have?

      int x;
      auto x = 0;

      verses…

      var x : int;

      Hmmm…. Honestly I don’t get it! Is there any type of ambiguity in “int x” that introducing ‘var’ keyword might help to eliminate? Or is it your personal taste only?

       
      • Alan
      • It could help with the most vexing parse (http://en.wikipedia.org/wiki/Most_vexing_parse) and it could help with readability (there’s more discussion in the paper I linked to above, they had a similar proposal).

         
        • Alex Korban
  12. Like the idea behind it. I think it would be cool to have a massive syntactic sugar overhaul for C++ in similar ways to Nimrod and Moonscript do to C and Lua.

     
  13. I think it would be a good idea to keep the Python “:” which introduces a new indentation level (“if a:” instead of “if a”). It gives a nice flow look. This would also combine nicely with the “public:”, “case:” declarations, and it would make it all more consistent.

     
    • gigi
  14. I really like the idea of using indentation as braces. On thing that bothers me in python is empty lines, people try to use it to “organize” the code and ends up making it less readable.
    I would enforce a empty line as closing brace.

    First thing I would do now is write a simple parser that transform this metaC++ in C++… .mpp to .cpp :]

     
    • Tiago Bonetti
    • I don’t know, in longer functions it’s good to break things up a bit with empty lines.

       
      • Alex Korban
  15. Have you taken a look at the Nimrod language (http://nimrod-lang.org/)?

    Personally, I find it’s the best attempt yet at a modern succint syntax in a fast compiled language. It looks very close to what you’re describing here.

    And IMO its feature set is a lot better thought out and implemented than most of the new languages being hyped today (Go, Rust, even Scala).

     
    • rigo
    • Very interesting, thanks.

       
      • Alex Korban