Dependent Types
Tuples can be used to create limited dependent types.
For example, we can create a range type that ensures both values are correct in terms of each other,
public readonly partial struct PositiveInt
{
[Refinement("The value must be positive")]
private static bool Positive(int value) => value > 0;
}
/// <summary>
/// This is a range type that is always composed correctly, its starting value
/// must always be less than its ending and will always produce a positive size.
/// </summary>
public readonly partial struct Range
{
[Refinement(
"The value must be a valid range where the start value is greater than the end value, '{value.End}' is not greater than '{value.Start}'",
Name = nameof(Range)
)]
private static bool Predicate((int Start, int End) value) => value.Start < value.End;
/// <summary>
/// Size of the range
/// </summary>
public PositiveInt Size => PositiveInt.Parse(Value.End - Value.Start);
}
Which can be used like so,
var range = Range.Parse((Start: 1, End: 4));
Assert.Equal(3, range.Size.Value);
// the range can never be a negative size
range = Range.Parse(
(
Start: -21,
// must always be greater than Start
End: -3
)
);
Assert.Equal(18, range.Size.Value);
// invalid ranges cannot exist
var isValid = Range.TryParse((Start: 6, End: 1), out _, out var message);
Assert.False(isValid);
Assert.Equal(
"The value must be a valid range where the start value is greater than the end value, '1' is not greater than '6'",
message
);