Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can type instantiation be separated from value instantiation?

Tags:

chapel

In C++, I can instantiate a generic type at compile time and then construct it at runtime:

struct Vertex {};
struct Edge   {};

template<typename NodeType, typename IdType>
struct Wrapper {
  IdType id;

  Wrapper(IdType id) : id{id} {};
};

int main() {
  using vertexWrapper = Wrapper<Vertex, int>;
  vertexWrapper v(3);
}

The variables are clearly separated, and types never look/feel like values. I am trying to do something similar in Chapel:

record Vertex {}
record Edge   {}

record Wrapper {
  type nodeType;
  type idType;   
  var id: idType;

  proc init(id) {
    this.id = id;
  }
}

type vertexWrapper = Wrapper(Vertex, int);

var v1 = 3: vertexWrapper;

When I compile this code, I get:

chpl -o graph-add-inclusion --cc-warnings test.chpl
test.chpl:9: In initializer:
test.chpl:10: error: can't omit initialization of field "nodeType", no type or default value provided
test.chpl:10: error: can't omit initialization of field "idType", no type or default value provided

Is there a way to separate type construction from value construction in Chapel to achieve an effect of a tagged type as I am trying to get in my example? I use tagged types to have single implementation that's common for two kinds of entities (vertices and edges here), but I want these entities to be different types.

There is a further, related question. Should I be able to just write:

type vertexWrapper = Wrapper(Vertex);

and then have integer deduced separately from my constructor?

It seems that constructors are checked at definition time without the possibility that types can be provided separately from values. Did I get this right, and, if I did, is this something that will change in the future?

like image 868
Marcin Zalewski Avatar asked Apr 19 '18 21:04

Marcin Zalewski


1 Answers

The issue you are encountering is with the initializer you have defined, rather than the type alias you are using. Because of how you have defined it, a user could conceivably try to write this:

var x = new Wrapper(1); // type is inferred from the new's return

and the initializer wouldn't have a clue what to do about the nodeType and idType fields. So your initializer needs to explicitly set them within its body.

I agree that it would be nice to have the initializer figure out their values when you are using an instantiation, and anticipate that this is something we will support in the future. In the near term, you can mostly get what you want by a little bit of duplicated work.

To do that, first you could update the initializer so that it can figure out the idType based on the corresponding argument.

proc init(id) {
  idType = id.type;
  this.id = id;
}

However, that doesn't help you with the nodeType field, since there is no corresponding value field the initializer can rely upon in what you've described. Which means that you will have to provide it to the initializer by hand. You can do this in a general way from your type alias by accessing its nodeType field:

var v1 = new vertexWrapper(vertexWrapper.nodeType, 3);

But you'll also need to update the initializer to take that as an argument. So creating the instance v1 will have to look something like this:

record Vertex {}
record Edge   {}

record Wrapper {
  type nodeType;
  type idType;   
  var id: idType;

  proc init(type nodeType, id) {
    this.nodeType = nodeType;
    this.idType = id.type;
    this.id = id;
  }
}

type vertexWrapper = Wrapper(Vertex, int);

var v1 = new vertexWrapper(vertexWrapper.nodeType, 3);

Hopefully that's a solution that works for your use case in the near term.

Should I be able to just write:

type vertexWrapper = Wrapper(Vertex);

and then have integer deduced separately from my constructor?

We don't support partial instantiations at this time, so Wrapper(Vertex) isn't something you can write. If you want idType to be set to a common value like int, you can provide it as a default value to the type field:

type idType = int;

which would allow you to just specify nodeType via Wrapper(Vertex), but would mean that idType is locked into int unless you create the instantiation using new (var x = new Wrapper(Vertex, 3.0);), or unless you specify something else at the same time as nodeType.

like image 137
Lydia Duncan Avatar answered Oct 02 '22 16:10

Lydia Duncan