Is There Anyway to Declare a Function Parameter of Int of Certain Range Like 0...25?

I want to be able to say:

func foo(number: Int<of: 0...20>) {
}

foo(100) // compile time error

Unfortunately, no.

However you can check for it at the beginning of foo

func foo(number: Int) {
  precondition(0...20 ~= number)
  ...
}
2 Likes

It is probably better style to use contains though:

precondition((0 ... 20).contains(number))

While technically fine in this case, ~= has rather fuzzy semantics. That line means “assert that number triggers the switch case defined by 0 ... 20” The definition of a switch trigger may be something besides containment for other types.

~= is designed so that a type can implement it to integrate with switches, but I don’t think it is really intended to be called directly.

4 Likes

O...k, thanks!

I'm doing precondition now.

I also tried this:

enum Number: Int { case a0, a1, a2, .... a20)

func foo(number: Number) {
    doCalc(with: number.rawValue)
}

foo(.a15)

The code look odd..

Right, that's a valid concern. I've been using them a little too liberally.

If you want to wrap the value for checking, that's also a way to do it. Though I'd prefer a single-value struct.

struct Number {
  var value: Int
  init(_ value: Int) {
    precondition((0...20).contains(value))
    self.value = value
  }
}
2 Likes

I got unexpected result with ~= in this:

"0"..."9" ~= "55" <==== this expression is true!

I don't understand why. Is the compile somehow casting "55" to "5" (Character)?

I just tried:

("0"..."9").contains("55") <========== is also true!

  • ... creates a range of anything Comparable.
  • "some string" is the syntax for string literals.
  • String is Comparable.
  • String’s sort is undefined. It is supposed to generate an order that is fast, not necessarily one useful for sorting the way users would expect for their particular human language. (I think it is currently byte order or something similar).
  • The strings end up sorted in this order:
    • "0"
    • "55"
    • "9"
  • The string "55" is between the strings "0" and "9", so the range’s implementation of ~=, which happens to use contains, returns true.

P.S. Maybe you just used strings by accident, when integer literals are what you wanted:

0 ... 9 ~= 55 // false
(0 ... 9).contains(55) // false
3 Likes

Is there a reason why String is comparable? I guess it could be useful for comparison-based data structures like search trees, is that the main idea?

This validation struct is the right approach, IMO. It's like the difference between URL and String.

One thing I would change, however, is to make the initializer failable. Otherwise it's just another precondition, like the original code, but in a different place. Most likely, consumers of the API will be forced unwrapping (like with URL.init(fileURLWithPath)), but it's good to give them the option

1 Like

What does precondition() do when it is false? For example, if Number here is initialized to 21?

It depends. Read the docs: precondition(_:_:file:line:)

IIRC it uses lexicographic order.

It's a little hard to know with this little context. If the validation doesn't appear that prominently, even the assert would be fine. Wrapping comes with its own maintenance cost.

It crashes in most environment, except for -Ounchecked which by its name skip some checking. To add to that, I find the following link to be useful:

And as a rule of thump, use assert when you check your own variable, use precondition when you check other's variable/user input, and use fatalError when it's... well, fatal.

Yes. It is so that those kinds of uses are possible. The String documentation has this to say about it:

Basic string operations are not sensitive to locale settings, ensuring that string comparisons and other operations always have a single, stable result, allowing strings to be used as keys in Dictionary instances and for other purposes.


That appears to approximately be the case, but it must also jump through hoops to respect canonical equivalence and other Unicode complications. Ultimately the String documentation only says this (along with the above):

Comparing strings for equality using the equal-to operator (==) or a relational operator (like < or >=) is always performed using Unicode canonical representation. As a result, different representations of a string compare as being equal.

It promises to do something stable and Unicode compliant. But that is all it promises. For example, lexicographical order of NFC and NFD would be different, and it doesn’t say which it does. As a client of the standard library, one’s options are to (a) experiment, (b) look at the source code, or (c) not rely on any particular ordering assumptions. Option C is most advised.


I’m not sure if that was just a typo, or if it is a language learning opportunity. In the the latter case:
The phrase is “ruleɹul ofʌv thumbθʌm”, where “thumbθʌm” ends with a silent B. The word “thumpθʌmp” means something else.

I took (b), Swift 5.0 (as well as master) uses NFC, at least on the fast path, but (c) is tempting as well.

That said, at this point, it’s probably prohibitive to change the semantic, and one could try to SE to add it to documentation.

That it is, and they’re not even close on the keyboard. I’ll leave it there in embarrassment.

I’m not sure what stable means, it’s a very specific term for sorting, not comparison.

Funny that I do [θʌmp] on both of them, with lighter [p] on thumb.

How did you write above the main line like that? Even Discourse's quote feature doesn't know how to replicate what you did :p

You can view the raw text of a post. In this case, it is:

The phrase is “[<ruby>rule<rt>ɹul</rt></ruby> <ruby>of<rt>ʌv</rt></ruby> <ruby>thum<strong>b</strong><rt>θʌm</rt></ruby>](https://en.wiktionary.org/wiki/rule_of_thumb#English)”, where “[<ruby>thum<strong>b</strong><rt>θʌm</rt></ruby>](https://en.wiktionary.org/wiki/thumb#English)” ends with a silent B. The word “[<ruby>thum<strong>p</strong><rt>θʌmp</rt></ruby>](https://en.wiktionary.org/wiki/thump#English)” means something else.
4 Likes

@Nevin is correct. A subset of HTML tags work on these forums. I first learned it was possible when someone used a <table>. I sometimes pull out <strong> or <em> when the Markdown parser refuses to pair asterisks properly. I’ve found uses for <sup>erscript, <sub>script and <small>. And at some point I decided to see if it could handle <ruby>.

1 Like

I've never even heard of <ruby>, wacky!

Bless the poor souls who need to implement browser renderers to handle at this crazy shit lol

1 Like

We’ve implemented the “wrapper struct” approach mentioned upthread in our library here: Refinement types by peter-tomaselli · Pull Request #11 · wayfair-archive/prelude · GitHub

as “refinement types”, which are fairly simple, pretty powerful, and not too weird. It’s the cleanest way I’ve found to do this in Swift so far. Fee free to steal anything you think might help!

Peter

1 Like

Since Swift 5.1 you will be able to use property wrappers and it will perfectly fit your use case. Check out example use cases and implementations here: