-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
As shown in #1268 (and to an extent #130), several users have requested some form of comptime interfaces/contracts/concepts/typeclasses/insert-terminology-here. While the current status-quo, duck typing, is sufficient to provide the desired functionality, it does not offer many clues as to what the parametric function is looking for, and errors thrown for performing an illegal operation on the passed-in type can occur in the middle of large function bodies, obscuring that source of the problem (the function was passed an inappropriate type).
I propose a change to the var
keyword allowing it to take a parameter of type fn(type)bool
. At comptime, when a new type is passed in the parametric parameter, the compiler will evaluate the function against the type of the passed parameter and, if the result is false, throw an appropriate compile error identifying the constraint function, the passed parameter type, and the call site of the parametric procedure.
std.meta.trait
, recently merged in #1662, provides several simple introspection functions that could be used in this way:
const trait = std.meta.trait;
pub fn reverseInPlace(list: var<isIndexable>) void {
...
}
pub fn createWorld(allocator: var<hasFn("alloc")>) []Things {
...
}
const isVector = multiTrait(
TraitList.{
hasField("x"),
hasField("y"),
hasField("z"),
}
);
pub fn dot(a: var<isVector>, b: @typeOf(a)) @typeOf(a) {
...
}
But since the constraint functions are completely user-definable, they can be arbitrarily complex.
pub fn isChildOf(comptime P: type) trait.TraitFn {
return struct.{
pub fn trait(comptime T: type) bool {
switch(@typeId(T)) {
builtin.TypeId.Pointer => {
const info = @typeInfo(T).Pointer;
switch(info.Size) {
builtin.TypeInfo.Pointer.Size.One => {
if(@typeId(info.child) == builtin.TypeId.Array) return meta.Child(info.child) == T;
return info.child == T;
},
else => return info.child == T,
}
},
builtin.TypeId.Array,
builtin.TypeId.Optional,
builtin.TypeId.Promise => return meta.Child(P) == T,
else => return false,
}
}
}.trait;
}
pub fn indexOf(list: var<isIndexable>, item: var<isChildOf(@typeOf(list))>) !usize {
...
}
See this gist for an overly complex example.