isContained(in array:) extension of Equatable

I agree with Pierpaolo's linguistic framing of the issue. I wrote a few months ago about the difference between Swift's collection.contains(element) and Python's element in collection.

To sum up, Swift likes to expose how methods are dispatched. The Swift community loves discussions about method dispatch. We're dispatching-obsessed. Hence collection.contains(element) instead of element.in(collection). That's because the language shows who decides of the actual implementation (the collection, not the element).

Now, I do lack some kind of in operator sometimes. Clear method dispatch is great, but code legibility would sometimes be enhanced with some reversed test.

4 Likes

I think this is a pretty important point when looking at the alternative if x in y.

From my point of view, Swift leans more towards avoiding overt natural language influences in how it expresses most syntactic constructs. The most obvious of these imo is the explicit rejection of and, or and not in favor of more traditional operators. The biggest exception I see to this is for-in, which is a nice trade-off between a traditional computer sciency loop, and how someone with no prior knowledge of how loops work in programming languages might try to express executing some bit of code on each member.

Now, If we were to introduce some kind of if x in y in operator, it would immediately beg for allowing if x not in y, in order for the negated form to read more naturally, over requiring !(). Now !() isn't horrendous, but it still seems that if we were to introduce this kind of construct, we would be eroding that division Swift has carefully crafted between natural language constructs, and traditional C-influenced languages.

Edit: Although, I could see !in being some kind of compromise between the desire for a natural language expression and traditional C style negation.

1 Like

My thoughts, exactly.

Now I'm totally neutral on whether it's good or not. I just think that:

  1. Such change would represent a drastic change both in the language, and in the mind of Swift developers
  2. Such change is hindered by the steep learning curve of Swift method dispatch, even for seasoned developers. As long as even seasoned developers struggle to reach full understanding of Swift dispatching, seasoned developers will be conservative about the knowledge that was so difficult to learn. Such "magic" will appear as non-essential, if not plain wrong, and thus rejected.

Edit I'm no linguistics or psychology expert - please excuse my subjective take on the topic.

if x ∈ [x1, x2, x3] { /* ... */ }

2 Likes

That is what I use in my code. I have ∈ defined for arrays, sets, and ranges and use it all the time. It will surely become a standard operator in Swift at some point.

It is easy to read because the single variable is on the left and the list of values is on the right (similar to assignments, which are written that way for the same reason).

2 Likes

I highly doubt that as long normal keyboards don't die out and get replaced with display keyboards. I can't even find that character on my keyboard layout. It's either super duper hidden or not present on my keyboard layout at all. That's just my opinion though so take it with a grain of salt. ;)

1 Like

There is no spoon.

Ukelele keyboard editor
• macOS system text replacement

(In my set‐up, “∈” is 3 keystrokes, “contains” is 8. Someday I’ll get around to releasing it to the world.)

But you are correct that there is immense community resistance against accepting anything from outside ASCII into the language or standard library. It was not a realistic pitch, just an extension suggestion for anyone else so inclined.

3 Likes

Ooh, a fellow Ukelele-ist!

On my custom layout it’s ⌥⇧L to enter Logic mode, then P to type ∈ (not sure why I put it there exactly. Probably so it’s next to ⋃ (U), ⋂ (I), and ∅ (O). E was already taken by ∃.)

• • •

I think the larger issue in this thread has to do with inversion of method calls during a functional-style chain. Until we have a good story there, I don’t think it’s worth special-casing this sort of thing.

For those who want it, the generic implementation of the OP’s request is trivially composable from existing features:

extension Equatable {
  func isIn<S: Sequence>(_ s: S) -> Bool where S.Element == Self {
    return s.contains(self)
  }
}

And the variadic version too:

extension Equatable {
  func isOneOf(_ s: Self...) -> Bool {
    return s.contains(self)
  }
}
2 Likes

This thread is quickly devolving into "What kind of sugar and/or obscure operators should replace the standard ~= operator", and would like to offer the same advice that I have been. If you want to use an operator that makes sense to you, just add an extension. Chances are that a beginner would (1) expect Equatable to only be used for ==, and find the contains method on array faster, (2) eventually learn the ~= operator and (3) not have the ability/knowledge to include special characters on their keyboard and will assume that there is no functionality that they can be provided with.

I am not calling everyone out, but this thread was supposed to discuss "Is it worth it adding an extension to Equatable" and if you feel like you want to discuss new sugar for Array.contains it would be best to start another thread.

1 Like

Ok, maybe we then should end the discussion here. The main arguments for and against have been made.

It seems that it is too controversial to add an isContained(in array:) , isAtLeastOne(from: ...) or the even better solution isIn from @Nevin (thank you for the suggestion) to Equatable in the Standard Library.

Of course people can add the proposed extension for themselves if they like it. The disadvantage of this is that everybody will do something that is quite common in a different way.

I myself dislike the if in [array] alternative because as been said by @nuclearace what about not in? Something like isIn returns a Bool and can be easily negated. Also isIn is more easily discovered.