Closed AdamSpeight2008 closed 8 years ago
I really like the idea!
I would love to better understand this proposal, so starting from the grammar i have few questions:
does a specification
allows multiple spec
? (the + make me aware of) or should we keep it fixed to one and use "trait inheritance" to allow a trait to be a composite trait?
Does we allow multiple traits to be specified as constraints?
Does we really want to always pay the runtime validation even when all clients of our method are known at compile-time?
Why there are some syntactic changes to how some concepts are represented, e.g <: instead of : for inheritance and a java like arrow type (->) instead of the familiar =>? Are those typos?
I'd like to use a trait in a similar way I'd use an interface:
public trait Zero<T> {
static T Zero { get; }
}
public trait Add<T> {
static T operator +(T,T);
}
public trait Subtract<T> {
static T operator -(T,T);
}
Inheritance:
public trait SimpleCalc<T> : Zero<T>, Add<T>, Subtract<T> {
}
Usage:
T Summation<T>(IEnumerable<T> nums)
where T has SimpleCalc<T>
{
var total = T.Zero;
foreach( T num in nums)
total = total + num; // += can't be used as the specification didn't specify it.
return total;
}
I personally won't bother about value traits and leave this kind of checks/validations to the Method contracts.
For features like this you may want to start from outlining a possible implementation. Otherwise you may end up with a spec for a feature that's not implementable.
@ilmax The symbol +
means one or more, so a specification
requires at least one spec
.
Those aren't typos. It is F# syntax (T,T)->T
()->() // Method takes no parameters, returns no parameters (void)
()->T // Method takes no parameters, returns a T
T->() // Method takes one parameter (a T) and returns no parameters (void)
T->T // Method takes one parameter (a T) and return a T.
The use of square brackets are for operator symbols, it easier to write and understand than the "true" names.
The actual syntax used and grammar, at this stage it a lightweight to get the concepts across.
@fubar-coder Yes, can use them like interfaces. The usage location of a trait is similar to that of interfaces and constrained generic eg where T is IComparable<T>
, because of it's location it could have visibility of the method's parameters. Hence a possible way for Method Contracts (#119)
So the definition of 'spec' would change to 'spec ::= trait_spec | contract`
T!
would be an implied value trait for non-null.
@mikeln I don't have the required skills or knowledge on my own, to implement it.
Another example trait
trait ICMP_Ops : IComparable<T>
{
[==] (T,T)->bool
[!=] (T,T)->bool
[<] (T,T)->bool
[<=] (T,T)->bool
[>] (T,T)->bool
[>=] (T,T)->bool
}
Traits could also be useful in-conjunction with pattern-matching. borrowing Nemerle's match construct.
match ( foo ) with
{ | __ has foo_trait -> ... ;
| __ has bar_trait -> ... ;
| __ has baz_trait -> ... ;
| __ has trait { ... } -> ...;
| __ -> ; // default match
}
I like this very much. It allows the generics in C# to be much more flexible, allowing structural typing for methods and classes that use the traits.
This is a really good basis to develop other features on top of in the future. For instance it'd be nice if you could have automatic traits, where the compiler infers the traits based on the usage within the class/function. That would eliminate a LOT of the boiler plate of types in C#, and give you essentially global type inference in opt-in scenarios.
Range Trait
T Item(int index)
{
trait { _a!; range: 0 <= index < _a,Length; }
get { _a[index]; }
set(T item) { _a[index] = item; }
}
Grammar
range ::= "range:" range_value op range_value op range_upper ";"
range_value ::= Expr<T : IComparable<T>>
range_upper ::= Expr<T> | IList<T>
range_op ::= "<" | "<="
Also thinking of a shorter version for collections.
range: _a;
So if it is a collection it has .Length
so an alias for _a!; range: 0 <= index < _a.Length;
Non-Null Trait
named_type ::=
op ::= '!'
nonnull ::= named_type op ';'
Trait Options
option traits // default traits are ignored at compile and runtime.
option traits -c -r // flag are -c compile-time -r runtime.
These determine how the traits are treated, by default are ignored both at compile and run time. The ide would give some indication that traits are being ignored if the source contains any. -c Enforce at compile-time. -r Enforce at run-time
This give the ability for the developer to decide if the want them and when.
Revised trait grammar (incomplete)
trait ::= "trait" identifier? commands? '{' spec+ '}'
commands ::= (compile runtime?) | (runtime compile?)
compile ::= "-c" | "-C"
runtime ::= "-r" | "-R"
spec ::= range_trait | has_trait | requires_trait | ensures_trait | shape_trait
range_trait ::= "range:" expr range_op expr range_op expr ';'
range_op ::= "<" | "<="
has_trait ::= "has:" '(' predicate ')';
ensures_trait ::= "ensures:" preicate ';'
shape_trait ::=
The trait options commands can now be place on a trait, to locally override a the global trait options. Shape Trait is for check the shape of something, does it have this method, does it have this interface. etc
@mirhagk Traits are not just for generic constraints.
Think of traits as being a separate concept to
Since traits are on more abstract level and should be considered as a part of the type/ object / method's signature. How the those traits are validated aren't specified.
isnot null
or obj !
bound:
If not trait type is specified (eg requires:
or bound:
it defaults to mean requires:
has this trait type is most likely to be used more often than the other, which permits shorter definitions in those use case.trait { x < y; y > x;}
vs trait { requires: x < y;
requires: y > x;
}
Both are styles are permitted.
Traits also get along well with Pattern Matching (especially #191)
trait xy { | (_,_) into {x,y} when (x < y) && (y > x); }
public void M(int x, int y) requires x < y, requires y > x {}
could be
public void M(int x, int y)
trait XY
{}
BNF Functions Use the arguments passed in to replace the arguments use in the rule definition.
/* BNF functions */
Seq<T,S> ::= T (S T)*
Sur<T,S0,S1> ::= S0 T S1
Comma<T> ::= Seq<T,','>
Par<T> ::= Sur<T,'(',')'>
Braced<T> ::= Sur<T,'{','}'>
PComma<T> ::= Par<Comma<T>>
BComma<T> ::= Braced<Comma<T>>
Eg PComma< ... >
produces '(' ... ( ',' ... )* ')'
/* */
constant ::=
variable ::=
type_identifier ::=
Trait Grammar
/* Trait */
trait ::= trait_key trait_options
trait_key ::= "trait"
trait_options ::= (compile runtime?) | (runtime? | compile)
compile ::= "-C"
runtime ::= "-R"
trait_body ::= '{' types_of_trait '}'
trait_types ::= named_trait | unnamed_trait
named_trait ::= identifier trait_body
unnamed_trait ::= trait_body
type_of_trait :: trait_inherits? ( requires_traits
| pattern_trait
| bound_trait
| shape_trait )+
Trait Inheritance
/* Trait Inheritance */
trait_inherits ::= trait_key '<:' ( named_trait | BComma< named_trait >) ';'
Requires Trait
/* Requires Trait */
requires_trait ::= "requires:"? requires_spec ';'
Trait Pattern Match
/* Pattern Match */
pattern_trait ::= '|' clause into? when?
clause ::= simple_clause
simple_clause ::= PComma< clause_types >
clause_types ::= simple | typed
simple ::= constant | variable | wildcard
wildcard ::= '_'
Parameter Type
/* Parameter Type */
typed ::= specific_type
specific_type :: simple ':' type_identifier
inherits_type :: simple "<:" type_identifer
Into Paramete
/* Into Parameter */
into ::= "into" into_params
into_params ::= BComma< identifier >
When Parameter
/* When Parameter */
when ::= "when" when_predicate
when_predicate ::= simple_predicate | complex_predicate
Bounded Trait
/* Bounded Trait **/
bound_trait ::= "bound:" bound_value bound_op bound_value bound_op ';'
bound_value ::= constant | variable
bound_op ::= '<' | '<='
Shape Trait
/* Shape Trait */
shape_trait ::=
shape_types ::= operator_shape | method_shape
scope ::= static? public | private
public ::= "public"
private ::= "private"
static ::= "static"
Type Signature This is using a lightweight syntax.
/* Type Signature */
generic_params ::= '<' Comma< generic_param > '>'
generic_param ::=
type_signature ::= generic_params? PComma< type_identifer > type_returns return_type ';'
type_returns ::= "->"
return_type :: void | type_identifier
void ::= "void" | "()"
Operator Shape
/* Operator Shape */
operator_shape ::= scope '[' operator ']' type_signature
operator ::= math_operator | comparision_operator
math_operator ::= minus |
positive |
add |
multiply |
divide
minus ::= '-'
positive ::= '+'
add ::= '+'
subtract ::= '-'
divide ::= '\'
multiply ::= '*'
comparision_operator ::= lt | le | eq | ne | gt | ge
lt ::= '<'
le ::= "<="
eq ::= "=="
ne ::= "!="
gt ::= '>'
ge ::= ">="
Method Shape
/* Method Shape */
method_shape ::= scope method_identifer type_signature ';'
This grammar probably requires more tweaking, but should be sufficient to gain an basic understanding.
Its an interesting idea, and I would like to see this working with extension methods over any class that have specific traits, but one thing concerns me:
At first glance this trait idea to me it looks like an interface, except:
I think it could be more interesting to add this "power" to interfaces, like allowing you to add static methods / properties to interfaces, operators support, etc. Along with support for duck typing.
Sample
interface ISample {
static readonly ISample Zero { get; }
static ISample operator +(ISample c1, ISample c2);
}
public static bool IsZero<T>(this T value) where T match ISample {
return T.Zero == value;
}
interface IOldStyle {
bool Test();
}
// Extension method that affects all classes that matches IOldStyle
public static void DumpTest(match IOldStyle obj) {
Console.WriteLine("Test: \{obj.Test()}");
}
Any comments?
Interface are a contract on the type (that implements them). Change the interface an you break things. A trait doesn't require any interface, it can be used against sealed types, either struct or class. As long as the trait is satisfied it can be used. The inheritance is use on traits to allow you specify more complex traits without having to write all it.
trait op_add { public static [+](T,T)->T ; }
trait op_sub { public static [-](T,T)->T ; }
trait add_sub { <: { op_add, op_sub }; }
A trait on an extension method.
Foo <T> ( this T x )
where T has trait add_sub
{
}
It's an interesting idea to leverage the existing interface mechanism. It would limit some of the more crazy traits proposed (value based traits wouldn't work for example).
Now you are creating a near vanilla interface, and saying that something matches it at the call site. That's an interesting concept. I'd consider as an alternative having an implict interface
so that rather than matching at the call site you simply use the interface as is.
Things to investigate are how well this works at the CLR level, does this cause any confusion, diamond of death type scenarios. Does it cause any backwards compatibility issues (thinking about reflection here)? Does it cause any more boxing of structs? (the nice thing about the generics approach is it can avoid boxing)
@jvlppm In your example you would have to specify that the operator ==
is also declared on the type.
A useful trait to have.
trait num
{
type:
{
| Integer ;
| Int16 ;
| Int32 ;
| Int64 ;
| UInteger ;
| UInt16 ;
| UInt32 ;
| UInt64 ;
| Byte ;
| UByte ;
| Single ;
| Double ;
| Decimal ;
}
}
The extension method Sum
would be a generic implementation.
T Sum<T>(this IEnumerable<T> xs ) where T has trait: num
{ T res = T.Zero;
foreach(T x in xs)
{ res = res + x
}
return res;
}
@AdamSpeight2008 Your example uses only Zero
and +=
from num
. If you require just those two members Sum
could support complex numbers, vectors, matrices, polynoms ... too
@d-kr That's a good point! It's good to see others are thinking about trait and improve a trait.
Maybe include the (int foo #0,string bar #1)
syntax of this Codeplex Topic
I've been thinking about traits recently. The big difference between a trait and an interface is that an interface has to obey the Liskov Substitution Principle (return types are covariant, argument types are contravariant), while a trait shouldn't.
For example, you cannot define IMonad<T>
in C#. Non-generic IMonad
can be defined using CRTP as IMonad<M> where M : IMonad<M>
, but this of course depends on the faithfulness of the implementor of the interface. Let's say you want to implement only a Bind/SelectMany method (since we cannot define static methods in an interface yet):
interface IMonad<M> where M : IMonad<M>
{
M Bind(Func<object, M> func);
}
class Maybe : IMonad<Maybe>
{
Maybe Bind(Func<object, Maybe> func)
{
//implementation goes there
}
}
You cannot define IMonad<M, T>
and implement it in Maybe<T>
, but you are welcome to try.
Another way out without traits is adding as this
type identifier (like Current
in Eiffel). This of course violates LSP and is a thorn in Eiffel programmers' side.
interface IMonad<T>
{
as this<T> Bind(Func<T, as this<T>> func);
}
class Maybe<T> : IMonad<T>
{
Maybe<T> Bind(Func<T, Maybe<T>> func)
{
//implementation goes there
}
}
IMonad<int> m = new Maybe<int>(1);
m.Bind(x => yield x); //BOOM!
So it looks like the only solution is having traits, which do not answer the question what the class is, but what it works like:
trait TMonad<T>
{
static as this<T> Unit(T value);
as this<T> Bind(Func<T, as this<T>> func);
}
class Maybe<T> : TMonad<T> //not necessary, but allows the compiler to check that the implementation is complete
{
static Maybe<T> Unit(T value)
{
return new Some<T>(value);
}
Maybe<T> Bind(Func<T, Maybe<T>> func)
{
//implementation goes there
}
}
Another point where I wished for traits was computing an average of Point
s using LINQ. I worked around by implementing a Point
-specific extension method, but a universal extension method could have been implemented with a trait.
One interesting thing is that it is closer to meta-programming (it will compile optimized methods against the actual type & direct calls instead of sharing implementation -- useful to build performance-critical algorithms with good code reuse).
I don't really want to put this proposal down, but it feels to me that this isn't what C# should be striving for. My issues with this are:
However, I could have misinterpreted your idea and in such a case, please feel free to prove me wrong.
@zdenek-jelinek It's similar the C++ proposal of Concepts. It a lot more than a language feature.
I've look at the other proposals
etc and seeing if there is a underlying unifying "concept" that is applicable. Traits I believe are good solution to that problem.
I think of the proposal as the essence abstraction of generics, contracts, Concepts (C++), fixes the issue think you can express with interfaces since they require instance methods, what if your need a static methods like an operator? Traits would allow them be checked and allowed to be used within that method. How do you define that a generic type argument but be Numeric? What do we mean by Numeric? Operator are overload able so strictly speaking there meaning is user defined How can we be certain that the operator `<' actually means LessThan? or that '+'' is to mean Addition. I
Interface <: Trait /* which only contain instance method traits /*
Contract <: Trait
Is it a Struct? Class? Interface? or none of the above. We need a way express these "higher level" concepts and axioms, down into the language itself. Where it can verified by the compiler at comple-time that those axioms are true. The CLR itself could then enforce them at runtime. It would also be possible release just the traits as a nuget package.
@AdamSpeight2008 I have thought about this and changed my mind.
As you are saying, this is very close to the Concepts of C++ and it makes sense to have a separate language concept for that.
I still feel there are some things that you may want to address:
public trait Number<T>
{
static T MinValue { get; }
static T MaxValue { get; }
static T Zero { get; }
}
public trait Addable<T> : Number<T>
{
static T operator+(T a, T b);
}
public static class SumProvider
{
T Sum<T>(IEnumerable<T> values) where T : Addable<T>
{
T result = T.Zero;
foreach (var value in values)
{
result += value; // uses operator+
}
return result;
}
}
This however does not fit all the needs you showed in this thread.
Note that the Zero
property is not currently present on basic numeric types. Thus we need a mechanism to add it there. We could probably use extension methods or extension properties (theoretical) to achieve such a thing.
Also, it would be nice to get some idea how this would be implemented in the type system and language itself. When you need to know whether a given type implements a given interface, you query it's inheritance hierarchy and see it there, that is quite fast. However for something like traits, the type needs to be checked whether it contains all the required functionality. These checks sometimes need to happen in runtime (instantiating generic type through reflection) and we need them to be as fast as possible. This doesn't really bode well with the extension methods...
As I see it, there are multiple issues with C# that can be solved with introducing duck typing and this is one good way of doing that (the other being for example type interfaces, #2427). The only difference I see in the concepts is that type interfaces reuse already existing C# features, while this brings a whole new feature in quite an aggressive manner. It would be nice to see both the proposals mature and be able to recognize the pros and cons of both the apporoaches.
However, I disagree with the idea of traits being solution for contracts etc. as mentioned above. Those are semantic checks evaluated regardless of evaluated type.
@zdenek-jelinek I think in some case it would be possible to get reflection based information at compile-time.
FYI, I implemented for fun the operator generic constraint (with an old version of Roslyn). https://improvecscompiler.codeplex.com/documentation Don't know if it's the way you want it but I +1 the idea.
I like the proposal but it can get confused with the existing concept http://en.wikipedia.org/wiki/Trait_%28computer_programming%29. I would change the title to "Trait-like contracts for generic constraints" or something else that mentions contracts and/or generics constraints.
People seem to be assuming that this is focused on generics and or contracts. There is no requirement the object be generic.
Think of traits as being a separate concept to •Interface •Class •Struct •Generics I consider them as being a further generalisation abstracts of Generic Constraints, Code Contracts, Method Contracts. As these approaches are in general about checks and validation, does the object / type met some form of specification.
Since traits are on more abstract level and should be considered as a part of the type/ object / method's signature. How the those traits are validated aren't specified.
As see it and proposed that these are aspects of the same problem.
Suppose you want to see if a particular object has specific traits ( C++ Concepts ) but that object is sealed / you have no control over its design and implementation. Eg you can not add an interface / contract to it. Extension methods don't help here.
How do I validate that object meets the specification? It may not implement an Interface
For example: Is it a numeric type?
What if I need to validate it has static
traits?
For example: A Static constructor that takes an Integer?*
@AdamSpeight2008 Sure, if you like the word criteria bettern than constraints. In either case this isn't what computer scientists call traits, and it would be less confusing if you select a different word.
@gafter Not every programmer is a computer scientist, if fact it is a rather specialist role. I'm thinking more towards the computer programmer.
noun: trait; plural noun: traits a distinguishing quality or characteristic, typically one belonging to a person.
synonyms: characteristic, attribute, feature, quality, property; habit, custom, mannerism, idiosyncrasy, peculiarity, quirk, oddity, foible
- characteristic
too long, easily mis-associated with character.- attribute
- Already has a meaning within C# and VB.net. feature
- doesn't work, as it as an existing association to aspects of system (typical UI).
- quality
Association with how good / bad something is.- property
Already has meaning within C# and VB.net- criteria
More associated with filtering data, or the conditional used when you know the measurement domain it being use.
eg height. > 6foot
Don't like the word *criteria as is has negative connotations, ie critic, criticism*
Possible Alternatives
@AdamSpeight2008 I think "concept" is probably the best of those.
How are traits implemented in a type? Addable
@ilmax
How are traits implemented in a type? Addable in a Vector2 type for example. What do you mean? Addable?
I have the two following traits:
trait [+] { static [+] (T,T)->T }
trait Plusable { static [+] (T, T) -> T }
And the following struct:
public struct Vector2
{
public float X { get; }
public float Y { get; }
public Vector2(float x, float y)
{
X = x;
Y = y;
}
}
Can I implement both traits in this struct, and if so, how would I do it?
Ideally everything everything could be disjoint:
public struct Vector2
{
public float X { get; }
public float Y { get; }
public Vector2(float x, float y)
{
X = x;
Y = y;
}
}
trait Addable { static [+] (T,T)->T }
public static Vector2 Add(Vector2 a, Vector 2 b)
{
return new Vector2(a.X + b.X, a.Y + b.Y);
}
attest Vector2 has Addable
{
Add is [+];
}
/*These declarations could be split across four different assemblies for all I care*/
The compiler checks your attest
statement and allows Vector2 to be passed to any function that accepts Addables.
public struct Vector2
{
public float X { get; }
public float Y { get; }
public Vector2(float x, float y)
{
X = x;
Y = y;
}
}
That type Vector2
does match either of traits because it does not have
static Vector2 operator +( Vector2 a, Vector2 b) {}
static Vector 2 Plusable( Vector2 a, Vector2 b) {}
You could also state that you implement the trait, like you implement an interface.
public struct Vector2 :
trait [+], Plusable
{
public float X { get; }
public float Y { get; }
public Vector2(float x, float y)
{
X = x;
Y = y;
}
static Vector2 +( Vector2 a, Vector2 b)
{
throw NotImplementedException();
}
static Vector2 Plusable( Vector2 a, Vector2 b)
{
throw NotImplementedException();
}
}
Think of the case where you don't control the type. (eg 3rd Party implementer), but you're a consumer of it.
@AdamSpeight2008
I consider them as being a further generalisation abstracts of Generic Constraints, Code Contracts, Method Contracts. As these approaches are in general about checks and validation, does the object / type met some form of specification.
I would call them clause
which means stipulation: a condition or requirement that is specified or demanded as part of an agreement.
@AdamSpeight2008 Isn't a central aspect of traits that they are always explicit? A better example:
trait DotProduct { static [*] (T, T) -> Scalar }
trait Scale { static [*] (T, T) -> T }
Both are different interperations of how the multiplication operator should work for a vector type. Xna for example uses dot product whereas every single other implementation uses scale (that I know of).
Could this be expressed?
edit: DotProduct should return a scalar type.
@GeirGrusom I don't think the signatures of your traits are correct. It should be:
trait DotProduct
{
static [*] (T, T) -> Scalar;
}
trait Scale
{
static [*] (Scalar, T) -> T;
static [*] (T, Scalar) -> T;
}
The question is still valid, since if we allow implicit trait implementation, Double or Int32 will suddenly have these traits. If trait implementation is explicit, no one will stop the programmer when they write attest int has DotProduct
, but it's their own problem now.
Actually, there's a more interesting case here, generic traits:
trait DotProduct<U>
where U : Scalar
{
static [*] (T, T) -> U;
}
trait Scale<U>
{
static [*] (U, T) -> T;
static [*] (T, U) -> T;
}
Now we can have types like Vector3<int>
that must produce integer dot products and can only be scaled using integers. But is this enough to allow writing var b = new Vector3<int>(1, 2, 3) * 1.5;
and getting the compiler to understand it can safely convert Vector3<int>
to Vector3<double>
?
@orthoxerox The DotProduct signature was wrong (since dot product returns a scalar), but the other was correct.
trait DotProduct
{
static [*] (T, T) -> Scalar;
}
trait Scale
{
static [*] (T, T) -> T;
}
If I write
new Vector3<int>(1, 2, 3) * new Vector3<int>(1, 2, 3)
Is the result [1, 4, 9]
or 14
?
edit: for Vector3 you could have cross product as well, which would result in 0.
This relates to explicitness of traits. Should traits be implied?
Oh, you meant scale as in elementwise multiplication?
Like I've said, the traits should be explicitly assigned to the types, either during declaration of the type or via a separate statement. They should be able to implicitly recognize the members of the type they require, but two traits that do not inherit from each other shoudn't be allowed to recognize the same member unless the programmer tells them to do so. E.g.:
trait DotProduct
{
static [*] (T, T) -> Scalar;
}
trait Scale
{
static [*] (T, T) -> T;
}
attest int has DotProduct; //ok
attest int has Scale; //compiler error: "operator * is already assigned to trait DotProduct!"
/* the correct way:
attest int has DotProduct {* is *}
attest int has Scale {* is *} //won't anyone fire this useless programmer?
*/
For Vector3 a different situation would happen, since Vector3 doesn't have a Scalar
trait and you can't define both operators in a way that both traits can pick them up implicitly (since only their return value would be different). For example, you could write:
public struct Vector3 :
trait DotProduct {* is DP}, Scale {* is S}
{
public float X { get; }
public float Y { get; }
public float Z { get; }
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
static float DP( Vector3 a, Vector3 b)
{
throw NotImplementedException();
}
static Vector3 S( Vector3 a, Vector3 b)
{
throw NotImplementedException();
}
}
Now I could pass Vector3
to a function that accepts DotProduct
parameters and use *
there to call DP
, pass it to a function that accepts Scale
parameters and use *
to call S
. But I wouldn't be able to use *
on two Vectors3
directly, or use DotProduct&Scale
as the function parameter type.
@orthoxerox Would really like to see traits implemented that way (non-intrusive). Lot of code reuse possible and lot of possibilities for new optimizations (force devirtualization by using traits instantiations instead of interfaces, without having to duplicate code).
Since all examples given in this issue are as a constraint, I'm renaming this to clarify that is its purpose.
This is a dup of #154.
Traits
Traits are a specification a Type / Method must have to be valid. This specification is validation at both compile-time and runtime. The runtime cost maybe worth it for additional safety,
Basic Grammar
incomplete
A trait can be define in a separate construct, like a class / struct.
Examples
A trait can "inherit" other existing traits. eg.
This allows "constrained generics" over numeric types.
A trait can also be anonymously define at the point of use
Value Traits
Value Trait are checked against the value of a variable, if it possible to valid at compile-time it is, if not it is validated at runtime.
Note:
T!
could be an alias for the NonNull TraitI do require help to spec-out this idea, I think it has potential has *(as see it) that traits would also enable other current proposals / part of them.