Field | Value |
---|---|
DIP: | (number/id -- assigned by DIP Manager) |
Review Count: | 0 (edited by DIP Manager) |
Author: | Richard Andrew Cattermole [email protected] |
Implementation: | (links to implementation PR if any) |
Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") |
A signature is a static vtable representation of complex objects such as structs and classes. They primarily represent heap allocated objects but can take advantage of the stack for scope.
They are very good at presenting behavior and description of objects in a way familiar to D programmers while limiting template if constraints that is hard to read and understand. The addition could lead to cleaner more interchangable code, as well as seeing a higher uptake in -betterC codebases.
Signatures are a very uncommon language feature originating in the ML family, examples of this are Ocaml and SML.
A more recent example of signatures is Rust's traits or Swift's Protocols which both focus upon implementing a specification, not a specification matching an existing representation.
Alternative designs to D's structs is C#, which can inherit interfaces but not other structs or classes.
A design in the C++ community include a type verification mechanism in the form of concepts. Discussion in the D NewsGroup along with a WIP DIP for it. As an addition to static_if
proposal.
Supplementary code is provided by the author in the repository.
In the D community we utilize idioms and design patterns which emphasize template usage. Template usage enables high code reuse but it also causes code bloat with quite horrendous error messages. These supplementary outcomes are not positive for both new and existing users in understanding why a template does not succeed.
At the core of the problem, we aim to pass around types based upon a statically known interface without knowledge of what the implementation is during compilation. It is quite common when template conditions fail at a function level, to not know if it is because of a function specific specialization failure or a more general interface one of the arguments.
The goal of signatures in this DIP is to present a language construct that is familiar to the (S)ML developers but also ties into how it would be used by the D community. So in our case, signatures are stack heavy constructs that can be made from either structs or classes. They have a statically created virtual table that may be assigned functions at runtime with patching to make an implementation match the interface. This allows a runtime decision of the implementation but a static compile-time design for what it must describe.
Methods exist that do similar things like Design by Introspection. They do not become obsolete instead, they become stronger more useful in customizing behavior using more concrete types. Allowing for cleaner interfaces and most importantly, a focus on concepts not code "versions" of them.
- Construction: Is implicitly constructed given an appropriate class or struct instance.
- Implicit construction of a signature may occur during assignment or passing as an argument to a function call.
- Implicit construction works in two forms. First as direct assignment and patching for class instances and pointers to structs. Second is a memory allocation and move of the struct instance, should it be copyable. Where it will be assigned and then patched for the given vtable.
- If the implicit construction is going into a scope'd variable, no allocation needs to take place unless it is being returned from a function. If it is being returned from a function it will be malloc'd and then free'd when it goes out of scope.
- Should a struct instance not be going into a scope'd variable (or being returned without scope) it will be allocated into GC owned memory where it will be moved into, should it be copyable. If it cannot be copied, it is an error.
- If the signature has named parameters (and only named parameters), they will be inferred automatically as part of construction from an implementation.
- Members are virtual (except static methods):
- There may be fields and methods. These are pointers/delegates.
- Operator overloads may be used.
- Types, alias and enum work as is with existing structs and classes.
@safe
field/method may be assigned to a method without any attribution. Same with@trusted
,nothrow
,@nogc
,const
andshared
. Different linkage/calling conventions are supported by the compiler creating a small patch function.- A final method cannot be overriden by a child signature and ignores the implementation.
- An override method overrides a parent signatures declaration but does not replace the implementations or a final parent method.
- A method maybe have multiple template if function bodies. And may refer to
typeof(this)
. The first that is valid will be used as the method. If none match then the default (non-constraint) function body will be used.
this
will not refer to anything outside of a function body or constraint, and it must be virtual or it will be an error.- A signature may inherit from others, similar in syntax to interfaces in this manner. However the diamond problem is not valid with signatures. If a field/method is duplicated and is similar, it can be ignored. If it is different (e.g. different types or different attributes) then it is an error at the child signature.
- The first pointer a signature instance stores is to the data it represents. This can be accessed by
.ptr
the same as a slice. If this pointer is null, so is the signature instance. To check for this usev is null
. - Signatures may be cast up for their inheritence. This can be computed statically and does not require any runtime knowledge. However they may not be cast down again. Rules regarding const, immutabe, shared ext. still apply like any other type.
- Usage of signatures as return types and arguments obey the same rules associated with interfaces and classes inside said interfaces/classes hierachies which is covariance and invariance. Patch functions will be required to implement this however and have each version available for casting to parent signatures.
.sizeof
property on signatures, total size of signature instance, works identically to astruct
's.sizeof
property..offsetof
works on each member field and method (with help of__traits(getOverloads)
) of a signature.- Named parameters will support signatures as if it was a class or struct.
- The is expression in the form of
is(T == Signature)
will perform an exact match on a signature type (must have been template initialized if required). When the is expression is in the form ofis(T : Signature)
it will attempt to initialize the Signature to T as if it was an implementation. The following code must not failSignature(new T)
for whenis(T : Signature)
is true. - For template parameters
is(T : Signature)
will have the same behavior asvoid func(T : Signature)(T)
. Same withis(T == Signature)
variant.
Keyword:
+ signature
TypeSpecialization:
+ signature
AggregateDeclaration:
+ SignatureDeclaration
+ SignatureDeclaration:
+ signature Identifier AggregateBody
+ signature Identifier : SignatureInheritance AggregateBody
+ SignatureTemplateDeclaration
+ SignatureInheritance:
+ Identifier
+ Identifier , SignatureInheritance
+ SignatureTemplateDeclaration:
+ signature Identifier TemplateParameters Constraint|opt : IdentifierList AggregateBody
+ signature Identifier TemplateParameters : IdentifierList Constraint|opt AggregateBody
+FuncDeclarator:
- StorageClasses|opt BasicType FuncDeclarator FunctionBody
+ StorageClasses|opt BasicType FuncDeclarator FunctionBody FuncDeclaratorSignature|opt
+FuncDeclaratorSignature:
+ Constraint FunctionBody FuncDeclaratorSignature|opt
The primary breaking change is the token signature
is becoming a keyword.
Existing code that uses it would need to rename existing symbols and variables but nothing requiring major changes.
Copyright (c) 2018 by the D Language Foundation
Licensed under Creative Commons Zero 1.0
The DIP Manager will supplement this section with a summary of each review stage of the DIP process beyond the Draft Review.