August 23, 2012

Leng: Type inference

I'll be using type inference similar to Scala or the new auto word in C++11. I still need to investigate the details but syntactically, I plan to be using # to replace a type that needs to be inferred.

August 13, 2012

Leng: Syntax (1)

I've got to be convinced that one of the reasons that there's so many refactoring tools out there for Java and so few for C++ is the extremely difficult that it's to write a parser for the second.

The language is charged with old compiler tagging and at the same time some features that make it really hard to parse.

Semicolon

I've been writing quite a bit of Lua lately and I keep getting surprised when I go back to C++ and the compiler starts complaining about missing semicolons. I don't want them in Leng.
Instead, the delimiting character will be the new line, with two exceptions:
- Expressions in brackets (of any type) require the closing one before finishing.
- Assignment requires at least one value before finishing
Unfortunately, that doesn't allow us to do funky stuff like:
 a = b
  + c
instead, you'd be doing:
 a = b + c
 // or
 a =
  b + c
 // or
 a = (b
  + c)
Like Lua, I plan to accept semicolons if you ever want to use them, but believe me, 99% of the time, they are just clutter that just removes clarity from the code. After two days you won't miss them at all.

Now, I don't want to follow the Lua path completely here as I feel its a bit undefined. Lua tries to look ahead the next statement to see if it could be part of the current one and kind of appends it. That can lead to unexpected results like:
function f()
 return
 assert(true, "never called")
end
What would you expect to get here? Well, tabbing is deceptive in this example. The assert does fire and the function returns true (the value that assert returns). Even though Lua makes it a syntactic error to have any statement after a return except for the end of a block, look-ahead parsing bends the rule in a rather obscure way.

Preprocessor and Templates

As you could expect, the preprocessor won't be a part of Leng. Instead, I plan to expand generic programming beyond templates. I hope this will add a new level of versatility to the language while making it safer and easier to digest (for both, tools and humans).

For templates, as much as I love the look of angle brackets <>, I'm considering replacing them with square brackets []. Replacing classic C array syntax, there would be a special templated class Array[C, N]with an overloaded operator() to access the values.




August 08, 2012

Leng: boolean expresions on conditionals


Only expressions of pure bool type (no implicit conversions) are allowed on conditions for conditional and loop structures. Pretty much like Java. Oh, and the assignment operator does not return a reference to the lvalue. Which means you cannot do:
int a, b, c, d;
a = b = c = d = 0;
Tell me the truth how many times have you really needed (or even wanted) to do that anyway?
The bool conditions are maybe a bigger loss because I'm really used to do
if(myPtr)
and now I will have to do
if(myPtr != Null)
But I think having a simpler and cleaner rule is worth the extra code.

August 05, 2012

Leng: return types

I've been thinking a lot about return values in C++ and how I'd like it to work in Leng. It's a hard because the variable that is going to receive the value needs to be of the correct type. In C++ we have:
 T Foo1(); //(1) copy
 T& Foo2(); //(2) reference
 const T& Foo3(); //(3) const reference
You could also return a const value but that doesn't really make sense in the common case and I've yet to come across where it was needed and no more elegant alternatives were available.

I've previously discussed how I planned to deal with arguments and I'm wondering if there is an easy way to use the same system. After a while I've got an idea which needs some polish but that I think could make the system more consistent. Here it goes:

When you define a type T you are actually defining two types Ref<T> and Val<T> and one smart alias T. The compiler will automatically decide what real type the alias T actually refers to in each context.

Also, following the discussion about constness on arguments, I've decided to omit completely the const word and use & (ampersand) as the symbol for non-const.
For example:
 // variable in the current scope (temporary, local or argument)
 // translates to C++: T
 T& Foo1() { return T(); }
 // variable from outer scope (member or global)
 // translates to C++: T&
 T& Foo2() { return (*this); }
 // variable from outer scope (member or global)
 // translates to C++: const T&
 T Foo3() { return (*this); }
This has several problems. The most obvious:
- The compiler is doing more stuff behind your back which you need to be aware of for rare situations.
- Breaks the compile model for C++ because suddenly the signature of a function depends on the declaration which means that even if I define the methods in a cpp file, if I change the variable being returned, the client code may need to be recompiled (or an error for invalid return type triggered). This means unexpectedly longer compile times and more difficulties when making changes to library code that has already been distributed. Although this is a problem in C++ too (if you change the returned variable you'll probably  have to change your return type from T to const T& anyway), it's less obvious when making changes in leng. Compile times are my biggest concern here as I believe they really affect programmer productivity in when working with big code bases.

The benefits:
- Most obviously, makes declarations more simple and readable.
- You can still use the unaliased types, which means you can have a tighter control on what the compiler is going to end up generating, if you need it:
Ref<T> Foo3() // translates to C++: const T& Foo3()
{
 // return (*this);
 return T(); // compiler error : returning address of temporary variable.
}
Of course, this needs all the conversion rules between the Ref and Val types, which are pretty much the same as exist in C++ for conversion between variables and references to variables:

leng C++
Ref<T> const T&
Ref<T&>  T&
Val<T>  const T
Val<T&>  T

Compiler translations will be deduced contextually:

alias translation context
T Val<T> return current scope variable (temporary, local or Val argument
T  Ref<T> return outer scope variable (member, global or Ref argument)
T Val<T> member declaration
T Val<T> local declaration
T Val<T> argument declaration where T is a fundamental type
T Ref<T> argument declaration where T is a user defined type

And equivalent for T& with Val<T&> and Ref<T&>

What a fundamental type is, however, is still to be defined.

There is also another missing point: in C++11 we now get rvalue references and move semantics. I still need to think on how to include them in this variable model but it's something that definitely needs to be there.

August 04, 2012

Leng: arguments


There are several ways to pass a variable to a function as a parameter in C++. For example:
void Foo(T x); //(1) copy
void Foo(const T x); //(2) copy
void Foo(const T& x); //(3) reference
void Foo(T& x); //(4) reference (really, an output parameter)
// I'm leaving pointers out of the equation for now

Constness

Arguments should be const wherever possible unless they are output parameters.
This makes refactoring functions substantially less painful in exchange for a small amount of typing in a few cases. That gets rid of (1).

Value or reference

The problem here is that it depending on how much it costs to make a copy of T, the programmer needs to decide if it's better to take something by reference and just copy. Over the years, I've noticed that 99% of the code I've read tends to follow the pattern: if T is a fundamental type (int, float, int*, char..) or an alias (typedef) for one, then use copy, otherwise use reference.
In very very few cases there will be a situation where you need to pass an object by value because of aliasing problems. I've never seen anyone pass a fundamental as a const&.
A long while back I started to to think it'd be nice if the compiler could simply apply that rule for me. That should make (2) and (3) undistinguishable at first sight.
That unloads some thinking every time I write or read a bit of code in exchange for hiding some stuff. The problem with hiding stuff is that can lead to rare and obscure bugs (for example problems with aliasing are less obvious). However, if the rule is clear (if fundamental then copy, else reference), the information is still there, it's just not poking at you every time you read the code.

Because const is the common case, it doesn't make sense to force the programmer to add an extra keyword for it, which means that those four declarations have been reduced to:
void Foo(T x); // translates to (const T x) or (const T& x) depending on T.
void Foo(T& x); // an output parameter

August 02, 2012

Introducing Leng

So I've been thinking a lot about programming languages lately. This kind of fantasizing is common to almost all the programmers I've met, specially those that tend to specialize in one language.

I think this could be a good space to jot down my ideas for a new programming language. I don't want to get too excited as this will probably end up in nothing so I'll be using the most boring code name I could think of: Leng. That's a short for "lenguaje", the Spanish word for language.

The kind of thing I have in mind is a simplified C++ like language. That means statically typed and multi-paradigm (object oriented + procedural) without automatic garbage collection plus a few extra additions.