Thanks for digging these up!
Yes, I think your “isReader/isWriter” point is spot on. I can picture this being more useful for high-level interfaces, such as isForwardIterator, etc…
One example I have is for implementing something like the strategy/factory pattern where we inject dependencies into a builder that returns a struct with our desired components. Likewise, iterator interfaces and general compound types that need to have several fields in one spot would help. I also think this is helpful in cases where *anyopaque member variables are involved as well. Since we’re losing type information, we can add constraints to the interface if we so choose.
In terms of this:
pub fn dot(self: anytype, other: @TypeOf(self)) @typeInfo(@TypeOf(self)).Vector.child {
return @reduce(.Add, self*other);
}
That return type is quite gnarly as is, so I don’t think much besides a comptime helper function to unpack that would be helpful. So for instance:
fn ElementType(comptime T: type) type {
return @typeInfo(@TypeOf(self)).Vector.child
}
But that essentially is its own constraint. It has to be a vector for that to even work so it’s probably not super useful here.
In your second example of equals, the only thing that comes to mind right now is the following…
fn hasPointer(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Optional => |opt| {
return hasPointer(opt.child);
},
.Pointer => {
return true;
},
else => false
};
}
And in the .Pointer segment, you could add your concepts to form the pointer constraint. Of course, we’d want to rename that concept at that point, but the general point is still there.
– edited to finish the example –
So for the equals concept, it could be like this…
fn hasPointerToChunkPosition(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Optional => |opt| {
return hasPointerToChunkPosition(opt.child);
},
.Pointer => |ptr| {
return hasFieldOfType(ptr.child, .pos, ChunkPosition);
},
else => false
};
}