F.A.Q.

why do sing symbols need to be fully qualified ?

 

You get less name conflicts and this makes intellisense more reliable and fast.

why are all the declarations prefixed by a keyword ? why all them end with } or ; ?

 

Because this makes it possible for the parser to skip statements and declarations. 
This capability is important to resync the parser after the errors and, most important, to just read from a file the public declarations skipping private declarations and functions bodies, which is a replacement for include files.

In var, type and let declarations, why does the type follow the declared symbol ?

 

It makes it easier to parse the declaration. The symbol is always present and the type is optional (in case of type inference). Parsing is easier if optional parts come last.

why don't sing retain the c++ types syntax ?

 

In C and C++ a type declaration is an expression that evaluates to a basic type. This used to simplify the design of the compiler in the old times but makes the declarations difficult to read for humans.
see 


http://c-faq.com/decl/spiral.anderson.html


to know more.

why reference counting ?

 

Having a garbage collector is not simply an options because you can't predict when and how long it will block the system.
On the other way manual memory handling, like in traditional C is unacceptably error prone. 
Reference counting is the reasonable compromise (is also the direction in which c++11 moved).

why in sing do I need to specify if an argument is an input or an output ?

 

First this is a part of the function interface that is worth declaring explicitly for self-documentation, then it helps sing using the best parameter passing method.

Increment, decrement, assignment and update: why are they  statements and not operators ?

 

This keeps expressions nearly free of side effects (many programmer have problem predicting the effect of multiple side effects in an expression - including me).
Also this makes impossible to mistakenly replace == with =.


Note that expressions can anyhow have side effects if they include functions.

why is 'for' so different than in c++ ?

 

In sing 'while' is the more general loop. 
Sing 'for' is meant to be more specific and simplify the writing of a very frequent case. 
Sing 'for' offers a compact, simple, readable syntax for regular loops and guarantees simple and predictable behaviour.
In C instead, both while and for are 100% general. C 'for' collects in a single line of code 3 statements you would otherwise need to place in 3 different places but apart that it makes no big difference with respect to while.
It doesn't make the 'for' any easier to read: not only you need to read and understand the three statements and figure out how they interact but you also need to read the whole block, because the index can be modified everywhere.
Additionally, C 'for' gives no guarantee that the loop will end or the number of expected iterations.

why is the 'switch' syntax different than in c++ ?

 

It makes 'break' redundant (if forgotten a source of bugs in C), allows anyway many cases to address the same statement. Forcing curly brackets when multiple statements are present, makes formatting more natural and clarifies which statements are grouped together (run all or none).

why did operators precedence changed with respect to c++ ?

 

To be easier to remember. I selected a few precedence levels and made explicit the reason why each operator should have a given level of priority. 

why expressions, in the general case, are not valid statements ?

 

If they have no side effect they have no reason to exist as isolated statements. Only function calls have side effects in Sing and only function calls are valid statements.
Additionally this triggers an error if someone replaces a = with a == operator.

why there are so few automatic conversions ?

 

Because automatic conversions are difficult to predict and can be a source of potential bugs (sometimes they don't work the expected way).

why do I need to qualify enum cases labels ?

 

To prevent collision with other enum's cases, which is pretty frequent.

why do I need 'this' to access class members from class functions ?

 

The right question is 'why c++ allows implicit access to the this pointer in member functions ?'.
To make writing more concise. This made the language less clear to newbies, the members prone to shadowing by local variables and arguments, the code less readable, to the point that programmers are forced to use special prefixes or postfixes to identify members.
I decided the advantage was not worth the pain and that 'this' was the logical replacement for any member name prefix.  

why there is no -> operator ?

 

Because it is a waste of programmer's neurons to spend even a fraction of second to select between '.' and '->'.
The compiler can infer easily what the programmer wants to achieve without a chance of misunderstanding.

why mut and not const ?

 

To encourage it's use. The programmer can overlook const but can't do the same with mut.

why is inheritance from concrete classes forbidden ?

 

My opinion is that inheritance from a concrete class is a confused feature that is easy to use in a wrong way (an anti-pattern). At a minimum:

  • it breaks encapsulation.

  • it is prone to the fragile base class problem.


Follow the links below to get an extensive explanation about another dozen reasons why you should avoid inheritance.
https://neethack.com/2017/04/Why-inheritance-is-bad/
https://codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117

why are there no constructors ?

 

They lead to overwhelming complexity (It can become pretty tricky in some situations. If you are not sure, you can enjoy the 30 pages section" Special member functions" of the C++ standard to learn how many fine details hide behind this seemingly simple construct)


Constructors gives two advantages:

  • No instance has an undefined content (if properly initialized by a constructor).

  • A constructor allows you to create and initialize an object at once. (which would otherwise require a variable declaration and a call to a standard function, eventually on the next line of code).

 

The first advantage is retained in Sing, even for variables that are not classes, even if the programmer doesn't declare a constructor, because the compiler default initializes all the variables that are not explicitly initialized.
As for the second advantage, I don't think condensing two lines in 1 is worth adding the complexity of constructors.

why is there no try...throw...catch error handling in sing ?

 

Throw is the mother of all the gotos ! It is a goto from a function to another (glomp !!). In my opinion the only reason why throw has not a bad reputation as goto has, is that it is perceived as new (which is wrongly assumed  'good' by some people !!).
Additionally, to fix a small problem (error codes verbosity), this construct opened the way to the more complex and insidious problem of exception safety.
I agree Sing should have a more concise way to handle errors, but for sure that is not the traditional try..catch construct.

why does sing not allow symbol overloading ?

 

well, based on experience I fully agree with this sentence: https://golang.org/doc/faq#overloading

why is sing so minimal ?

 

Whatever feature is added to the language, it must then be supported in the future and learned by everyone who approaches Sing.
That's why before adding any feature I want to be REALLY confident it will add value to the language.

why is sing so restrictive and simple ?

 

Because I value over any other characteristic Safety and Readability. After all what is asked to most programmers is to be efficient and to produce high quality code.


A restrictive language makes it difficult to make errors, a simpler language is better known by the programmer and is less likely to behave in unexpected ways.


A simpler code is also more readable and, since any code is read many more times than written, readability promotes efficiency much more than conciseness or expressiveness.

How do I replace Lambda/Closures in Sing ?

 

Replace it with an object

es:


do_something([a, b](int x, int y) { a += x; b += y; });

becomes

class mylambda {
public:
    var acc_x i32;  // better: make them private and write accessors.
    var acc_y i32;
    fn mut accumulate(x i32, y i32) void;
}

fn mylambda.accumulate(x i32, y i32) void
{
    this.acc_x += x;
    this.acc_y += y;
}

var topass mylambda;

topass.acc_x = a;       // optionally save captures in the functor
topass.acc_y = b;
do_something(topass);   // pass the functor
a = topass.acc_x;       // optionally retrieve captures from the functor
b = topass.acc_y;

Verbose, isn't it ? but works perfectly and is way more familiar to programmers not used to functional programming.

How do I replace inheritance in Sing ?

 

  1. Make the base and derived function inherit from a same interface if you whant to be able to use the second as it was the first.

  2. Embed the base class in the derived one (composition).

  3. Let the embedded class implement the function

interface BaseFunctions {
    ...         // the functions
}

class Base : BaseFunctions {
    ...         // the variables
}

class Derived : BaseFunctions by base_implementor {
    var base_implementor Base;
    ...
}

You can now pass around a BaseFunctions pointer without knowing if it points to a Base or Derived. 
If the object is a Derived, through the interface you are actually accessing its Base part (base_implementor).

 

Unlike in plain inheritance, should the need arise, Derived can control the access to Base. 
It would be enough to change Derived this way:

class Derived : BaseFunctions {
    var base_implementor Base;
    ...

    // BaseFunctions implemented by Base
    fn fun1 by base_implementor;
    fn fun2 by base_implementor;
}

// This function from BaseFunctions is implemented directly by Derived

fn Derived.fun3(...) void

{
}

There is no thing as a protected member in sing. If you want Derived to access private members of Base you must provide public accessors. On the other end, if Derived can modify some field in Base without compromising Base internal consistency, any other Base owner should be allowed the same.

Where does the name Sing come from ?

 

Well, I like to think the sing programmer is happily whistling all the day !
Also sing is the acronym of "sing is not go" which is fitting because sing was inspired in many ways from go but, because of the constrain of being compiled to readable c++, it grew pretty different.