Tardy
Runtime polymorphism without inheritance (structs, ints, classes, ...)
Install / Use
/learn @atilaneves/TardyREADME
tardy - runtime polymorphism without inheritance
What?
import tardy;
interface ITransformer {
int transform(int) @safe pure const;
}
alias Transformer = Polymorphic!ITransformer;
int xform(Transformer t) {
return t.transform(3);
}
struct Adder {
int i;
int transform(int j) @safe pure const { return i + j; }
}
struct Plus1 {
int transform(int i) @safe pure const { return i + 1; }
}
unittest {
assert(xform(Transformer(Adder(2))) == 5);
assert(xform(Transformer(Adder(3))) == 6);
assert(xform(Transformer(Plus1())) == 4);
}
Why?
Traditional inheritance-based runtime polymorphism has a few drawbacks:
- Classes must inherit the memory layout of their parent classes.
- Bakes in reference semantics.
- Must be careful with "copies" (actually references)
- All instances are nullable
- User-defined classes must be written with inheritance in mind so using third-party code is sometimes not possible without a wrapper. In other words, the set of types that can participate is closed.
- Mandatory heap allocations for the virtual table and the instance...
- ... which means ownership issues if not using the GC
- Doesn't work well with algorithms expecting values (consider sorting)
Louis Dionne explained it better and in more detail in his talk.
Tardy makes it so:
- Structs, classes, and other values (ints, arrays, etc. via UFCS) can implement an interface.
- The resulting instances have value semantics.
- An allocator can be specified for storage allocation (defaults to the GC).
- Function attributes can be specified for the generated copy constructor and destructor for instances.
Creating instances
Instances may be created by passing a value to Polymorphic's constructor or by emplacing them
and having Polymorphic call the instance type's contructor itself. The examples above show
how to construct using a value. To explicitly instantiate a particular type:
auto t = Polymorphic!MyInterface.create!MyType(arg0, arg1, arg2, ...);
One can also pass modules to create where Polymorphic should look for UFCS candidates:
// Using the `Transfomer` example above, and assuming there's a
// UFCS function in one of "mymod0" or "mymod1",
// this constructs an `int` "instance"
auto t = Transformer.create!("mymod0", "mymod1")(42);
Specifying function attributes for the copy constructor and destructor
The vtable type is constructed at compile-time by reflecting on the interface passed
as the first template parameter passed to Polymorphic. To not overly constrain what users
may do with their types (@safe, pure, ...), the default is @safe, but attributes
can be specified for each of these compiler-generated member functions:
interface MyInterface {
import std.traits: FA = FunctionAttribute;
enum CopyConstructorAttrs = FA.safe | FA.pure_;
enum DestructorAttrs = FA.pure_ | FA.nogc;
}
Related Skills
node-connect
354.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
112.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
354.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
354.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
