At the moment we can have constraints for generics parameters, I wonder what people think about constrains for the native numerical types?
I know it's related to Design by Contract but I think that this is a more simple problem and isn't so ambitious and it can always be expanded in the future either to support more types or/and user-defined types.
So what I'm thinking is something like this:
class Point
{
Point(int x where x > 0, int y where y > x)
{
}
}
Bad idea? good idea? what do you think?
I think that this falls firmly under Method Contracts. I certainly wouldn't want one way to enforce numeric "constraints" vs. enforcing method contracts. I'd suspect that 99% of method contracts would be validating non-null references and ensuring that numbers are within range.
@HaloFour
I certainly wouldn't want one way to enforce numeric "constraints" vs. enforcing method contracts.
I don't think that we will ever see method contracts and this is the reason I'm thinking about ways to somehow have a slim down version to at least have preconditions formalized as part of the language as constraints.
I'd suspect that 99% of method contracts would be validating non-null references and ensuring that numbers are within range.
This is the reason I'm thinking about it.
Non-null references can be or should be implied whereas range needs to be explicit so I thought that the way we express constraints for generic parameters can be a good fit.
I am very interested in constraints, but I would rather wait for general solution with method contracts.
...
Whenever it would be ready for implementation (C# 10/11/...)
@eyalsk
I don't think that we will ever see method contracts and this is the reason I'm thinking about ways to somehow have a slim down version to at least have preconditions formalized as part of the language as constraints.
Perhaps, but the reasons that method contracts might not happen would apply equally to this proposal. Specifically how the constraints would be encoded in the assembly metadata and exactly how the constraint is enforced when violated. Your proposal doesn't touch on either.
@HaloFour This isn't a proposal, just a discussion... 馃槃
@gordanr
I am very interested in constraints, but I would rather wait for general solution with method contracts.
I'd love it myself but seeing that there were many attempts and the design team still think it's a bad idea or that it's too complex or too verbose I'd say the odds aren't in our favour.
@eyalsk
This isn't a proposal, just a discussion... 馃槃
Either way, I'd argue that it's the most important aspect of the discussion.
Source generators could probably handle the simpler cases. Toss attributes on the parameters and the generator could emit replacements which perform argument validation.
@HaloFour
Source generators could probably handle the simpler cases. Toss attributes on the parameters and the generator could emit replacements which perform argument validation.
The issue is that two different implementations may generate different attributes for different projects and you may end up needing multiple source generators, another issue is you don't really want to _fail-fast_ at run-time, you want compile-time errors and analysis.
Now, we can probably implement this through analyzers but there's no formalized attributes that are standardized for this so each 3rd-party may have its own thing.
@HaloFour
Either way, I'd argue that it's the most important aspect of the discussion.
I'll think about it, I mostly think about having the information embedded as attributes but it's a bit _cumbersome_.
There are some high level questions like:
How operators are represented in attributes? ">" as a string or ContractGreaterThanAttribute as a type or IsGreaterThan as a method, etc..
How conditions are composed together? e.g., y > x || 0 > y
I think that the first part should be about having the information available so analyzers can take advantage of this or something like this.
_p.s. I think you want this information or at least prefer this information to be part of the PE metadata but I'm not sure what changes this require._
Maybe it is better not to work with construcors (special case) in constraint examples. Let we take general method with slightly changed syntax (#119).
class Point
{
public void DoSomething(int x, int y) requires x > 0 && y > x
{
...
}
}
@eyalsk I don't think that attributes are good approach to encoding constraints in assembly metadata. It is too complicate and impractical.
Compiler could easily generate private hidden method for every constraint and every constraint type (requires, ensures).
This method is hidden and always returns bool value.
Method is encoded in the assembly without losing any information about constraint.
Generated method is associated with constraint definition. Over it's name or in any other way.
This method is ready for any type of use (run time violation report or much better some compile time analysis).
Expression in constraint probably should not be part of the method signature, but it is shown in documentation and hints (similar as return type).
This approach doesn't require any clr change. (I hope)
class Point
{
public void DoSomething(int x, int y)
{
if (!$requires_DoSomething(x, y)) => throw MethodContractException(nameof(DoSomething));
...
}
private bool $requires_DoSomething(int x, int y) => x > 0 && y > x;
}
I put throw Exception in the case of violation, but it is open question.
I had focus on encoding constraints in assembly metadata.
@gordanr
I don't think that attributes are good approach to encoding constraints in assembly metadata. It is too complicate and impractical.
Yeah that is the reason I've closed this discussion, I've played with it and it seems really chaotic. 馃槃
Compiler could easily generate private hidden method for every constraint and every constraint type (requires, ensures).
This method is hidden and always returns bool value.
Method is encoded in the assembly without losing any information about constraint.
Generated method is associated with constraint definition. Over it's name or in any other way.
This method is ready for any type of use (run time violation report or much better some compile time analysis).
Expression in constraint probably should not be part of the method signature, but it is shown in documentation and hints (similar as return type).
This approach doesn't require any clr change. (I hope)
The best second place I can think about adding this information is xml comments but yeah the best place is in the PE metadata, the question what needs to change in order for this to take place and how complex this change is and then I'm not sure whether it impact compatibility.
I think that the approach you're proposing can probably be done with generators but it has the same problem as I pointed above
Most helpful comment
I think that this falls firmly under Method Contracts. I certainly wouldn't want one way to enforce numeric "constraints" vs. enforcing method contracts. I'd suspect that 99% of method contracts would be validating non-null references and ensuring that numbers are within range.