Skip to content

all containers anonymous #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
andrewrk opened this issue Jul 26, 2016 · 10 comments
Closed

all containers anonymous #160

andrewrk opened this issue Jul 26, 2016 · 10 comments
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase.
Milestone

Comments

@andrewrk
Copy link
Member

See previous discussion from #151.

Containers are structs, unions, and enums.

All containers anonymous makes the language even smaller and more elegant. However it presents a big problem to solve: naming.

  • Compile errors need useful names for containers.
  • Debug information needs useful names for containers.

Is the first variable you assign an anonymous struct to somehow more important in the name?

Should the name of the container be something the user explicitly has control over?

Should the function name and parameters be used to determine the name of the container? What if the function merely chooses between two existing types?

Is this maybe overkill and we shouldn't have all structs be anonymous?

Should containers accept a compile-time-string argument which is the name of the type?

@andrewrk andrewrk added the enhancement Solving this issue will likely involve adding new logic or components to the codebase. label Jul 26, 2016
@andrewrk andrewrk added this to the 0.1.0 milestone Jul 26, 2016
@andrewrk
Copy link
Member Author

andrewrk commented Jul 26, 2016

One idea is that we can take advantage of the type top level decl.

type Point = struct {
    x: i32,
    y: i32,
};

Now Point is a typedef for an anonymous struct.

The idea breaks down a bit when you move to generic containers:

inline fn List(inline T: type) -> type {
    struct {
        items: []T,
    }
}
fn foo() {
    var list1: List(i32) = zeroes;
    list1.append(123);
}

Here we declared list with a purely anonymous type. However you could do type ListOfInts = List(i32); to get a nice name. But that's more work than it is in other languages to get a name for generic types.

@andrewrk
Copy link
Member Author

Prerequisite to solving this is #169

@fsaintjacques
Copy link
Contributor

Is this maybe overkill and we shouldn't have all structs be anonymous?

I don't think enforcing anonymous-ness is such a great idea. What if you typedef with different name the same anonymous struct, which name is the compiler going to report? Requiring that all struct are anonymous is drastic. I'm suspecting that this feature will re-create the infamous C++ compiler unreadable error.

I prefer readability & maintenance over cleverness of implementation.

Is there a real gain here other than simplifying the language grammar?

@andrewrk
Copy link
Member Author

Is there a real gain here other than simplifying the language grammar?

Not really. We'd only do this if we solved the compiler errors and debug symbols problem satisfactorily. Then it would help push the "one way to do things" design goal.

Consider that this will already be possible. Even if we don't require all structs to be anonymous, it will still be possible to create structs with generic parameters in this way. So we kind of already have to solve this problem. And then if we solve it adequately, it's a simplification to make it the only way to do it. This is my logic.

Let me ask this, if the naming problems were solved and compiler errors were ergonomic, would you be in favor of this change? Is there another reason to not do it?

@fsaintjacques
Copy link
Contributor

fsaintjacques commented Oct 27, 2016

If ziglang ever want switch statement to do pattern matching (I hope it does!), it'll be hard to do with anonymous struct that has 2 semantically different structures with the same signature, e.g.

struct Expr;

struct ValExpr {
  value: bool
};

struct AndExpr {
    left: Expr;
    right: Expr;
};

struct OrExpr {
    left: Expr;
    right: Expr;
};

Only the name differentiate the semantic in the case of AndExpr and OrExpr, not the type signature.

@andrewrk
Copy link
Member Author

Can you give an example of how this pattern matching would work? It seems to me like the expression would be of a certain type, and testing if it was a different type wouldn't make sense. I think I just need a clarifying example.

@fsaintjacques
Copy link
Contributor

The language as is couldn't permit such construct, I'm thinking of scala's case class pattern matching. Could it be done with trait and implementing type?

trait MyTrait;

struct A impl MyTrait;
struct B impl MyTrait;

pub def traitToString(aTrait: Trait) -> []u8 { 
    switch(aTrait) {
        MyTrait => "I am MyTrait",
        A => "I am A",
        B => "I am B"
    }
}

maybe zig will never have this expressive power.

@andrewrk
Copy link
Member Author

Ah, thanks for the example. This is on the table. See #130

@andrewrk
Copy link
Member Author

andrewrk commented Dec 3, 2016

fn Foo(inline T: type) -> type {
    struct {
        item: T,
        bar: &Bar(T),

        fn member() {
            bar.member();
        }
    }
}

fn Bar(inline T: type) -> type {
    struct {
        item: T,
        foo: &Foo(T),

        fn member() {
            foo.member();
        }
    }
}

This code would need to work correctly if we did this all containers anonymous thing. How it would work is, when the anonymous struct expression is encountered while executing the inline function, we would create the container type and the scope for it, however we would not eagerly scan the declarations - we would leave those lazy like other declarations. So functions Bar and Foo can return their anonymous structs before the structs are analyzed. Then we proceed with lazy declaration evaluation like normal.

Considering that something like this is possible:

fn Foo(inline b: bool, inline T1: type, inline T2: type) -> type {
    inline var T = T1;
    const Result1 = struct {
        item: T,
    };
    T = T2;
    const Result2 = struct {
        item: T,
    };
    return if (b) Result1 else Result2;
}

If we do something like Foo(true, i32, f32) then this returns Result1 which is a lazily evaluated container. Note that the inline variable T changed. We would need to capture the value of T and any other inline variables when an anonymous container is instantiated, and make sure we can reference those values when we need to resolve the container.

@andrewrk
Copy link
Member Author

This is done and working and now merged into master branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase.
Projects
None yet
Development

No branches or pull requests

2 participants