# Allow for Compile-Time Checked Intervals for Parameters Expecting Literal Values

**URL:** <https://forums.swift.org/t/allow-for-compile-time-checked-intervals-for-parameters-expecting-literal-values/26743>\
**Category:** Pitches\
**Created:** [July 9, 2019, 2:14am UTC](https://forums.swift.org/t/allow-for-compile-time-checked-intervals-for-parameters-expecting-literal-values/26743 "2019-07-09T02:14:46Z")\
**Posts on this page:** 1\
**Showing post:** 9

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [July 21, 2019, 4:24am UTC](https://forums.swift.org/t/allow-for-compile-time-checked-intervals-for-parameters-expecting-literal-values/26743/9 "2019-07-21T04:24:41Z")

</div>

## TL;DR

Protocols express the requirements to _be_ something. Types express the notion that a thing _is_ something. Constraints (constexpr or otherwise) express the requirements to _be used for_ something. Depending on what you want in any specific situation, you choose the appropriate of those three mechanisms. Examples like the aforementioned ‘Height’ one tend indeed to want for [constexpr] constraints.

## Reasons

It’s interesting (and complicating) that what this is trying to express is not just typy nor just protocolly. In some sense you’re just trying to say “I want an Int that meets these requirements” - which sounds protocolly, but then when you think about it, does it make sense to have a `Height` protocol in the traditional sense, i.e. where technically it can be _any_ type, not just an `Int`? In a _pure_ Protocol world that could make sense, since `Int` would merely be a _protocol_ that expresses the _idea_ of a thing which has the set of properties & capabilities that distinguish an integer. But that’s nowhere near what Swift is today, and seems obviously not a place Swift will ever go.

If you nonetheless try pragmatically to just add the ability to require specific typeness to protocols, e.g.:

```
protocol Height: Int where self > 0, self < 10 {}

```

Or:

```
protocol Height where Self is Int, self > 0, self < 10 {}

```

There’s all sorts of awkward things about that. It blurs the lines between types & protocols, for one. You haven’t actually _conformed_ `Int` to the protocol either, yet - you’d have to add the extension to `Int` as well, which is a lot of redundancy & boilerplate. And in principle, since it’s a protocol, you can put all the normal protocol stuff between the curly braces, which… well, probably that’s actually useful in some cases, but it doesn’t negate the prior concerns and might amount to feature creep.

So that suggests this should be a bit more typy, e.g.:

**subtype** Height **is** Int **where self** \> 0, **self** \< 10

That seems much cleaner at the outset (even though it still blurs typiness & protocolness a bit).

A new keyword & associated concept would be required, as opposed to repurposing `typealias` or similar, because of the need to support _any_ type, not just classes, as well as the fact that this is _not_ merely an alias (as @CTMacUser pointed out). I think it’s also best to be distinct, syntactically, vs subclassing - i.e. not go down the route of allowing:

```
struct Foo {
    ...
}

struct SubFoo: Foo where self > 0

```

It’s too close to subclassing, and thus people will forever be misguided into wanting support for all the rest of subclassing’s capabilities (new properties, new functions, overrides, etc), which don’t make sense by definition for a constraining subtype (nor value types in general in the presence of the `extension` keyword & mechanism).

Alternatively there is a ‘wrapper’ approach, where you declare e.g. ‘Height’ as a new type which stores an `Int` internally and applies the conformances through constexpr constraints specifically on its `init` method(s), but that’s an impractical amount of boilerplate for any non-trivial base type, e.g.::

```
struct Height {
    private var actualValue

    init(_ value: Int) where value > 0, value < 10 {
        self.actualValue = value
    }
}

// And now a bajillion extensions to make Height conform to all the protocols
// expected of an Int, assuming there even are protocols to fully cover what
// an Int is (hint: there aren’t, today)...

// ...and you still have to do “myHeight.asInt” or somesuch everywhere you need
// to use virtually any existing code, since `Int` is a type, not a protocol. This
// approach is very hostile to the extensions paradigm.

```

So, given all that a `subtype` keyword et al seems like the best approach (at least vs protocols or wrappers).

Then, several obvious questions are:

### Do `subtype`s manifest as new `Metatype`s?

My intuition is that they should, for several reasons:

- Code that calls `type(of: ...)` and the like, and certainly anything that conveys the type of the thing to a human, would be more intuitive to call it a `Height` than an `Int`. It would also be a valuable distinction in reasoning about code using generics or compound types, since while _technically_ an e.g. array of `Height`s and an array of `Int`s would be equivalent at runtime, semantically they are distinct.

- Being able to runtime conditionalise behaviour based on _compile-time ensured_ and stated compliance to a subtype could be useful just as it can be useful to downcast class instances. e.g.:

- You can then more sensibly require explicit upcasting (i.e. requiring `someHeight as Int` whenever you pass the value to a parameter expecting an `Int`), because the true type would be preserved under the covers, allowing for downcasting back to the leaf type at any later stage (if needed).

### What does `<some Height> + <some Int>` do?

The resulting type cannot unconditionally meet the requirements, since the addition could produce a value that’s outside the requirements of `Height`. A reasonable default would therefore be to pick the least specific type, i.e. `Int` in this case, but that means we’ve added (back) into Swift implicit type conversion, essentially (granted it’s an upcast, not a lateral cast, but still).

But furthermore consider, instead of an operator, what about a method on `Int` that returns `Int` - what does that do when called on an `Int` that is specifically a `Height`? Does it still return an `Int`? It would have to - we can’t unilaterally guarantee that the result of all methods on `Int` that return `Int` (or `Self` if expressed that way) would actually return a value conforming to `Height`. And it would be a bit absurd to have to do `(myHeight as Int).someMethodOrProperty` for all such methods - you should just use the ‘wrapper’ pattern at that point, without any new language support needed.

And any mechanism that would require `Height` to override every such method in order to ensure the returned values are also `Height`s is impractical if not impossible - for example, how would it handle a `negated` method sanely when the result is naturally not a valid `Height`, but the method on `Int` is not declared with `throws` nor returns `Optional` or the like. Etc.

So logically any method on the parent type:

- Cannot implicitly return the subtype (not even via `Self` or any other such genericness).
- Must nonetheless be supported on instances of the subtype _without_ any extra boilerplate.

So at that point, what’s the point? Anything you do to a `Height` will turn it “back” into a raw `Int`, and that tiny remaining functionality - of semantic distinction in passing values around unmodified - can easily be achieved using a wrapper type.

Alternatively, if the compiler were willing to sort of ‘dynamically’ change the method signatures - e.g. automatically make throwable or the return value `Optional` any methods which aren’t provably going to return a a valid `Height` - then I guess that could work… but that’d be a pretty complicated task for the compiler (and might require constexpr and similar advancements first, anyway)...

### Conclusion (?)

...all of which seems to lead to the apparent conclusion that this should _not_ be implemented by actual subtyping (nor protocols), but instead ‘merely’ via constraints on values at their individual use sites (e.g. via constexprs).

What we then need is a way to declare a ‘macro’ for those constraints, to get comparable convenience & benefits to the `subtype` approach, e.g.:

```
constexpr func IsValidHeight(_ value: Int) -> Bool {
    return value > 0 && value < 10
}

...

func someFunc(_ value: Int) where IsValidHeight(value) {
    ...
}

```

Though naturally you still want to be able to apply the same constraints to stored values’ types, and now we discover an infinite loop since the apparent way to do that would be something like:

```
<some keyword> Height = Int where IsValidHeight(self)

```

That’s really just a convenient ‘macro’ for, at every stored value declaration:

```
var height: Int where IsValidHeight(height)

```

And then if you think about it _that_ way, maybe `typealias` really is essentially all that’s needed (given constexprs)…?

### Addendum

In any case, what actually interests me - beyond just ‘plain’ constexprs - is the idea that the Swift compiler could automatically de-try and de-Optional based on parameter analysis & the function’s implementation, where appropriate (i.e. not across boundaries that are supposed to represent stable APIs). This wouldn’t be a replacement for any of the above necessarily, but could be complimentary, e.g.:

```
func setHeight(_ value: Int) throws {
    guard let height = value where value > 0 && value << 10 else {
        throw SomeError
    }

    ...
}

...

setHeight(5) // Look ma, no ‘try’!

```

Swift knows the argument ‘5’ can’t cause an exception by static analysis, so it allows the ‘try’ to be elided. That’s the ‘carrot’ side - the ‘stick’ side could be similarly enhanced vs today:

```
try! setHeight(11) // Compile-time error: Swift knows it will always throw and therefore always abort.
try? setHeight(11) // Compile-time warning: Swift knows it will always throw.
// The try? case could even detect that `setHeight` has no side-effects, omit code for it
// entirely, plus warn that the statement has literally no effect.

```

To me that seems like a much bigger win in practice (even if it only works within a single module, as my intuition suggests, without more effort from the compiler and/or programmer).

Then, adding constexpr to the mix generalises this fully.

---

_[View the full topic](https://forums.swift.org/t/allow-for-compile-time-checked-intervals-for-parameters-expecting-literal-values/26743)._
