Implement "NonEmpty" collections

https://github.com/pointfreeco/swift-nonempty — For when a collection is guaranteed to have at least one value.

7 Likes

Can you elaborate a little bit on what you're pitching? Your link shows that a solution already exists and seemingly works well.

While I like the premise of this feature, I'm not exactly sure if it should have a place in the Standard Library. There are certainly collections that are guaranteed to be non-empty at compile time

let collection: [String] = ["Hello", "World"]

func foo() {
    print(collection.first!)
}

but much more often do I find I use collections that are empty for some period of time (pending network data, user inputted values, etc.), and by the time I go to recall a value from it, they're guaranteed to be non-empty.

var collection = [String]()


func foo(element: String) {
    collection.append(element)
    
    print(collection.first!)
}

Those are really the collections I would like to see benefit from this. Having to write the force unwrap operator can promote undesired coding styles, especially among new Swift developers.

To add, I also don't like having to wrap my types in another NonEmpty type. In an ideal world, this would be something that's directly inferred by the compiler without me needing to explicitly wrap the type. Alternatively, the [Element!] syntax is currently deprecated, and scheduled to be removed, perhaps we could retool that (or something similar) to mean "There's guaranteed to be at least one instance of Element when querying" instead of "Element is implicitly unwrapped"

Right, but would it be useful in the standard library? In the interest of minimizing third-party dependencies in software, and encouraging standardization across multiple variants of something similar to this, it's a possible addition.

I'm a fan of this. I think it would be useful to standardize.

My problem with .first! is that it doesn't distinguish between "I guarantee this won't be empty" vs "I assume this won't be empty, and I'm just being sloppy".

I like the idea of constructing a NonEmpty wrapper once, at which point the provided collection is asserted to not be empty, and from then on, having that guarantee enforced.

1 Like

I've actually pitched something like this about a year or two ago (can't find it on the forums now). The general consensus back then was that while there is some use for this, it doesn't pull its weight enough to be added to stdlib.

IMHO there are just a few cases where this may have added value:

  • first and last are always nonnil. first, however, is easily replaceable by [0]
  • indicating that a return value or a parameter is never empty - for example, String's components(separatedBy:). Changing this, however, would be a source-breaking change at this point and I believe that source compatibility is a requirement at this point already.

Last time this was discussed, the first point itself isn't strong enough to warrant this addition, the second point seems nice, but would introduce additional types and complexity to the language. At least that was the majority stand at the time of the original discussion.

2 Likes

I would be a +1 for this.

I would be happy to see this added to the standard library. I'm currently using something equivalent in a model that contains an array for which emptiness doesn't make sense. It's nice in the same way that non-optional values are nice -- you don't have to handle the empty / nil case when that's not a valid state to be in in the first place.

I think NonEmpty as a wrapper type is difficult, Although I would support adding more protocols for non-empty collections, if the bug that a property of type T does not satisfy a protocol requiring a property of the same name of type T? were fixed.

I think NonEmpty as a wrapper type is difficult, because one would have to be careful that:

NonEmpty<String>(first: "e").appending("\u{0301}") == NonEmpty<String>(first: "e\u{0301}")

Either with extra work in appending or ==.

+1 for me, too.

I think would be great is some kind of general modifier that let the programmer state valid values for a type. So, for example, the programmer could specify that a certain array is intended to hold between 4 to 8 elements, or a Double is expected to hold values between -1...+1. No checking on the finished app, but Swift would throw an error when running with the debug flag, and with static analysis.

This is not a problem at all if you store the underlying collection in a single property rather than separating the first element from the rest.