Author Topic: C-domain useful features  (Read 1734 times)

stefanos82

  • Newbie
  • *
  • Posts: 3
    • View Profile
C-domain useful features
« on: December 06, 2016, 09:49:19 PM »
Hello everyone,

I'm Stefanos and I'm really thrilled to have found this beautiful language.

Believe it or not, I planned to design a language once I find the time to finish Dragon Book. As soon as I have found C2 though, I have realized that around 85% of the whole language matches my personal notes.

I'm speechless and fascinated at the same time. Kudos Bas for your amazing work.

Now, allow me to share some personal thoughts of mine and please, by all means don't take them for granted or that they are the right way to do things.

Whatever I'm expressing below are fully my own out-loud thoughts and nothing more.

Here we go:

There are lots of things that look elegant and beautiful with C2, but there are things that need clarification, at least to me.

For instance, I have seen attributes being used at the end of a structure or a function, whereas in >= C++11 have been redesigned to be used at the beginning.

Its format is the following:
Code: [Select]
[[deprecated ("reason")]]
void echo(void)
{
    std::cout << "I'm grumpy." << '\n';
}

I feel a bit confused with the following example
Code: [Select]
type Person struct {
    const char* name;
    uint8 age;
} @(packed)

I will show an example about attributes at struct_functions.

Also, how come structures don't end with semicolon? I presume for readability purposes? ¯\_(ツ)_/¯

Another observation would be the "no ordering" declaration. In my humble, yet personal opinion, based on my experience this actually creates a number of headaches, to me at least, for finding certain variables' original location / declaration. Maybe I'm wrong here, but the top / down concept works better for most of people...

About public specifier...hm, this one is a bit tricky, but quite useful too I have to admit.

What I have had originally in mind (based on my language concept), was something like this:

Code: [Select]
module Foo;

// whatever is declared in here,
// it's accessible from just importing module Foo.
public {
    // example #1
    from stdio import printf;
    func void echo(void) { printf("Hello world!\n"); }
    // example #2
    func void echo(void) { stdio.printf("Hello world!\n"); }
}

// whatever is declared in here though,
// should be accessible via full module name - module member.
private {
    // everything in here would be accessed only with full module name use
    func int returnInt(void) { return 10; }
}

Let's try to use it:

Code: [Select]
import Foo as f; // automatically imports whatever is in public {} section.

f.echo(); // it should print "Hello world!"
f.echo("Hello"); // it should throw an error due to explicit use of void.

f.retunInt(); // it should throw an error for trying to access a private section.

// a new operator "::" is used to signify the use of private section
Foo::returnInt(); // this should work;

The private section concept is quite convenient in my opinion, because this way it would emulate the concept of namespaces, thus organizing code bases rather elegantly.

I was thinking about how come you haven't used auto as built-in type mechanism.

You could use auto along with lambda functions and build something monumental:

Code: [Select]
from stdio import printf;

type Point struct {
int32 x;
int32 y;

// example #1
func int32 add(int32 x, int32 y) {
this.x = x;
// it should work also as: .x = x;
this.y = y;
return (.x + .y);
// or return (this.x + this.y);
}

// example #2
// You can use this as your object pointer explicitly
auto subtract = func(this, int32 x, int32 y) {
this.x = x;
this.y = y;
return (this.x - this.y);
};
}

Point p;
int32 result = p.add(1,2);
printf("Addition result is: %d\n", result);

result = p.subtract(2,1);
printf("Subtraction result is: %d\n", result);

Don't you think auto and lambda (anonymous) functions can help with coding a bit?

Let's move on.

One thing I have noticed is that you use pointers. I know they are there for compatibility purposes, but what about introducing ref type?

This could lead to cleaner and safer code in case someone forgets to delete a reserved memory with malloc().

For instance, let's take a look at the following example, directly taken from the documentation about struct_functions:

Code: [Select]
module inner;

import stdlib;
import stdio;

public type Shape struct {
    uint8 sides;
    // ..
} @(opaque)

// a non-public struct-function
func void shape_init(Shape* shape, uint8 sides) {
   shape.sides = sides;
}

// a static struct-function, called as Shape.create(..)
public func Shape* shape_create(uint8 sides) {
    Shape* shape = stdlib.malloc(sizeof(Shape));
    shape.init(sides);
    return shape;
}

// a public, const struct-function, first argument: const Shape*
public func void shape_print(const Shape* shape) {
    stdio.printf("shape with %d sides\n", shape.sides);
}

// a public, struct-function, first argument: Shape*
public func void shape_free(Shape* shape) {
    stdlib.free(shape);
}

The following questions come immediately in my mind:

  • Why importing everything from both stdlib and stdio and not import what I want to use?
  • Isn't this a bit expensive?
  • Why release Shape pointer's memory?

Let's rewrite the code a bit and see whether it makes it even clearer:

Code: [Select]
module inner;

from stdio import printf;

@[opaque]
public type Shape struct {
    uint8 sides;
    // ..
}

// a non-public struct-function
func void shape_init(ref Shape shape, uint8 sides) {
   shape.sides = sides;
}

// a static struct-function, called as Shape.create(..)
public func ref Shape shape_create(uint8 sides) {
    Shape shape;
    shape.init(sides);
    return shape;
}

// a public, const struct-function, first argument: const Shape*
public func void shape_print(const ref Shape shape) {
    stdio.printf("shape with %d sides\n", shape.sides);
}

This last example could be combined with my previous explanation regards to auto and lambdas and could construct something elegant and useful in my humble opinion.

Last, but not least the import local could get replaced with my aforementioned private section. import local and local keyword can lead to confusion.

In case you wanted to avoid such confusion, you could either replace local with static or introduce a new keyword named inline.

That's it for now. I think I have pretty much shared what I think I would have done with my own language, but your milage may vary as they say.

Again, let me say that no one and I repeat this: NO ONE should take anything I have just said for granted.

Take everything with a grain of salt.

Cheers folks.
« Last Edit: December 07, 2016, 10:24:51 AM by stefanos82 »

bas

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: C-domain useful features
« Reply #1 on: December 08, 2016, 08:22:58 PM »
Nice that you like it!

Now about your remarks/question:

attributes
The syntax for type definitions is: 'type <name> <definition> <aliases>'.
This works quite well for some types, but might indeed look weird on enum and structs that
use {} in their definition. Since function definitions look similar to struct type definitions and
they use the syntax: 'func <return type> <name> (<params>) <attributes> { .. }', it might
be an idea to make struct/enum refs similar to functions so that it becomes:
Code: [Select]
type Point struct @(packed) {
   int32 x;
   int32 y;
}

What do you think?

semicolon
In regard to the remark about missing semi-colon after a '}', the rule is very simple: In C2 a '}' will never be followed
by a semi-colon. This is done because C sometimes requires it and sometimes not. It's also easier on the eyes.

ordering
Funny that you find ordering helpful. I just jump-to-definition in my editor and don't care much about where something is
defined. The design rule here is 'define everything once'. This is one of the foundations of C2. So no forward declarations,
so then you need a multi-pass parser since declarations cannot mutually refer to the other.

imports
One other design goal is to keep the syntax as simple/readable as possible since you spent a lot more time reading code than
writing it. So in my opinion, all the extra public {} is just plain ugly and unnecessary complex. Also it's important to think about
the semantics of such a thing. That is, what does it mean. That should also be very clear. Since C2 does no symbol mangling
like for example C++, some restrictions apply (for example no operator overloading on functions, etc).

So currently there are 2 access types:
- public - this means visible outside module (if imported)
- non-public - only usable from within same module.

The concept of a third one has been discussed earlier, namely a 'private' access, so only usable within the same file.
After consideration this was deemed to introduce nothing new and only make things more complex. Also consider the
no-mangling rule.

auto / lambda
Both the auto and lambda concepts have their place, but they simply don't fit into a c-like language. I
find the auto keyword annoying, since I cannot see the type directly. And the 'gain' is nothing much.

Lambda functions are very nice, but required functions as first class citizens to make them truly useful. This
just doesn't work in a C-like language. So keep it mind that C2 tries to improve C while keeping the same 'core'.

struct functions
You have a point about the syntax of struct functions. There might be a better syntax. The thing to keep in
mind is the no-name-mangling though. Also (quote) C2 is not C++ (unquote). So there is no viable and this
pointer in the sense of C++. Struct functions are purely syntactic sugar (but very nice nonetheless).

I am toying with the idea of adding the struct members in the namespace of a struct functions to avoid all
the 'this.' usage. I cannot see any real objections so far and the code does look a lot cleaner (and the same
as for example C++).

stdlib/stdio imports
When code in some file wants to use symbols from another module, it needs to import those. The 'import verylongname as van'
allows developers to make their own code look much nice, while allowing longer module names to be used. Imports are NOT
handled in the same way is C/C++ #include's. Those are much more expensive. In a C2 program, the stdio code is only parsed
once for the entire compilation of the entire program, not once per file. Also the symbol table is linked into the global symbol table,
a very cheap operation. If you worry about speed, you should definitely switch to C2  ;D
On my laptop C2 compiler parses around 1.5-1.8 million lines of C2 per second in a single thread!

Whow that was also a long reply. Your ideas were definitely useful. So we need to re-evaluate the syntax of struct-functions and
the syntax(position) of attributes for enum/struct types.

--Cheers--


stefanos82

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: C-domain useful features
« Reply #2 on: December 08, 2016, 09:22:48 PM »
Hey Bas,

Thanks for the reply.

I guess I need a humongous list of examples to see various cases of C2's use.

What I currently have in mind is a bridge between C and C++.

There are lots of things of C that I want to get rid of and there are lots of C++ features that I really don't like or need.

Is there anywhere any list of examples to test them myself?

Never mind, I have examined the examples from github source tree and they look interesting.
« Last Edit: December 09, 2016, 12:53:24 AM by stefanos82 »

bas

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: C-domain useful features
« Reply #3 on: December 09, 2016, 08:40:37 AM »

The 'examples' directory from the c2compiler git archive is indeed a more practical example. For more
extensive code fragments showing of specific features, you can look at the 'test' directory. That contains
the automated tests that test individual features.

The goal of C2 is to be a better C. For some problem domains, C++ would be a better choice than C. Let's
call this domain the systems-programming domain. Most big new languages like Go, Swift, D, Rust target
that domain. C2 explicitly does not, since you need a lot more higher-level features there, like some form
of object-orientation, some form of memory-management (even like C++'s destructor).

Even the C2 compiler is written in C++, which is a better choice than C for those type of applications.
I used to love C++, but the direction it's going is simply that of one big mess. And looking on the Internet,
I'm not alone in that opinion. I would love to see a combination of C++ and C2, say C2++ :)
But for C2, there simply cannot be a viable, runtime, etc.

Which features of C++ don't you like?







stefanos82

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: C-domain useful features
« Reply #4 on: December 09, 2016, 04:10:45 PM »
As a language C++ looks extremely sophisticated and it's a very powerful tool.

The problem with it though, is that the direction the standard committee is following goes beyond comprehension, at least for me, and even though certain features have their use, it creates an unnecessary chaos.

The features I don't like have been "silenced" so to speak with the introduction of auto and lambdas in C++11. For example, how iterators need to be initialized inside a for loop in C++98 syntax.

Still though, there are many chaotic libraries thanks to meta-programming designers that wanted to show off for some reason to their friends that they are smart and as a result we have a template mess.

Even Bjarne himself mention this on various presentations and asks for simplification of such libraries if they really want to attract more C++ users.

bas

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: C-domain useful features
« Reply #5 on: December 10, 2016, 09:26:03 AM »
Exactly, It's also a explicit non-goal to make the language help developers. What!?!?

With C2 my ideas is to restrict to language a bit more. This is similar to Python with the
one-way to do things. So if something is allowing in C, but in any of my project we would
disallow a certain construction (by agreement), in C2 this would simply be forbidden in the
language. This will make C2 programs more consistent and easier to read.