Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - stefanos82

Pages: [1]
1
General Discussion / 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.

Pages: [1]