[swift-dev] Set of "no less than one" member enforced by the compiler

Gwynne Raskind gwynne at darkrainfall.org
Sat Jun 25 18:54:03 CDT 2016


I’ve been spinning my wheels on this one for a couple of hours and can’t come up with a way to do it which doesn’t require checking for at least one potential data inconsistency at runtime with precondition() or assert(), so I’m wondering if anyone else has a solution.

I have a data type which is conceptually a Set (not an OptionSet, it’s not a bitfield and has no concrete representation, simply a Set). This Set may contain members chosen from a limited set of choices, which in Swift I represent as an enum:

enum Kind {
	case FirstKind
	case SecondKind
	case ThirdKind
}

typealias Kinds = Set<Kind>

The data to which this applies is a structure-

struct DataItem {
	let kinds: Kinds
	// other fields
}

This structure demands that the Kinds set must always contain at *least* one element, because conceptually "no kind" is not a sensible state for a data item. However, I can’t use just the enum because the data can be of more than one kind at a time. It can just never be of no kinds - in that case, there is no data at all, and I don’t need a state *within* the data to represent that, I would simply have no DataItem in the collection which holds the list of data items.

In a relational dataset, I would say that the "no kind" state was denormalized, because it can be represented by there being no row in a table. The other obvious alternative, to duplicate the DataItem for each Kind, is also denormalized (multiple rows whose data is not solely defined by the candidate keys). The less obvious alternative of having a map of [Kind : [DataItem]] (in a database this would be akin to having a kindID column in the DataItem table with a separate Kinds lookup table), while fully normalized, is not only inefficient and difficult to manipulate but also disassociates a critical piece of information about the item from the item itself. My in-memory data model is not a relational database.

Half-digression into relational theory aside, I can’t figure a way to make the compiler enforce this. An enum is "exactly one", a Set is "zero or more", a structure or class is "this group", an Optional is "zero or one". Obviously, I can do "precondition(kinds.count > 0)" to check this at runtime, but that admits that the data can exist in an inconsistent state. Is there some way, even a hacky one, to express in the data model itself (such that the compiler will enforce it) that there must be at least one "kind", as the enum already does for "only one at a time"?

There’s probably some bit of type theory which explains why this is either difficult or impossible in practice, but I’m hoping maybe it’s just a corner of knowledge I’ve yet to previously delve.

-- Gwynne Raskind





More information about the swift-dev mailing list