Join GitHub today
using EntwineTest to compare results when the underlying type is a tuple - kind of awkward #6
First off - I'm the last to say I'm an expert at Swift, or the vagaries of type theory and effectively using a statically type language with generics like we have.
I was creating some tests to using EntwineTests virtual scheduler to illuminate how some of the Combine operators work - specifically combineLatest and some of the others that merge pipelines together. (ref: heckj/swiftui-notes#56)
What I hit my shins on was that the equatable conformance with Entwine's Signal relies entirely on the underlying type's conformance - totally good, except that the type can be a tuple (and is, in the case of combineLatest, which is merging the output types of the upstream pipelines.
I came up with two options to do the validation regardless, although both are really quite ugly hacks. I'm opening this issue to see if you had any brilliant insights into how this might be done more nicely.
The first was the use the same testing hack I use for comparing Error enums - using the
The second was to create a one-off function that specifically broke down a TestSequence item (itself a tuple of VirtualTime and a Signal enum instance) and return a boolean value if they match up.
Although it's functioning and correct, it feels ugly and awkward, so I was hoping you might have some ideas on how this might be made more elegant.
Although the timing was very useful to show for my combineLatest tests, I also realized that the key of what I was interested in validating was the ordered list of data results, and a lot of the type information around it wasn't as interesting for my specific test.
I'm not sure if there'd be significant value in making a helper operator to pull out just a sequence of the OutputType and make that available from under TestSequence, but it might be useful as an idea.
Sure, I'll comment on the issue you linked to also, but so there's a reference here:
Tuples in Swift still leave a little to be desired, they don't auto-synthesise Equatable or Hashable conformance, and in fact there is no generic way you can conform a tuple to these protocols. From the discussions I've read from the Swift team, tuples often add an additional layer of complication when they're implementing new features and seem to be a general thorn in their sides!
However, they are particularly ergonomic from a coding perspective, so hopefully they'll resolve these issues at some point.
With EntwineTest, the solution I use to make the TestSequence conform to Equatable is to map the tuples to an intermediate struct, I then make that struct conform to Equatable and compare those instead.
You could generalise it, but as I'm only using it in the one place I didn't want to bloat the codebase.
But it's simple enough to do.
The theory behind it is that any tuple can also be represented as a struct. So if you can create an intermediate struct that maps from a tuple and back, you then should have all you need to conform to whatever behaviours you need.
The big thing to remember is that you will need an intermediate struct for each arity of tuple. So for two, three, and four element tuples, you will need the intermediate structs of:
In terms of implementation it's as simple as:
Specifically with Entwine, though I think the generalised Tuples solution is probably outside the scope, what would probably help is some additional mapping functions so If you have made some intermediate structs – you can use them with ease.
Enabling something like:
Does that help? I'll push those mapping utils to master so you can try them out.
thank you for the explanation, that helps considerably, and I'll definitely try out the utilities to map to structs and back. Once you know how to do this, and how to attack it (your description is excellent, for example) it becomes pretty clear to formulate a plan to put it all together.
There's a downside in that a lot of people won't either know or want to do the extra work involving making a struct for the intermediate tuple collections just to make them testable, so in the back of my head I'm a little concerned that they simply wont, and the powerful feature of this library would get missed or neglected (as well as crappier tests would tend to exist)
I apologize, as I'm sure it sounds like I'm complaining about the wonderful feature work and solutions you're providing here - that's not my intent. The core of the question in head is "how does this get made easily apparent to someone else coming in after me?" and I genuinely don't have a good answer there, even as it relates to the my writing/authoring work with Using Combine and other texts.
Glad it was useful, @heckj! And I totally agree, fwiw: It's not completely obvious how to solve these issues when you come across them. The challenge is deciding what should be part of this library and what should be outside of it.
If you make use of tuples frequently, then including a set of intermediate tuple structs in your project is probably a good idea. Maybe there is room for a library that includes just these intermediate structs. Historically I've avoided libraries for smaller utilities that can be copy and pasted into a project though. Maybe a gist?
But as you say, it isn't going to be immediately obvious to most people that this is what they need. Intuition would likely suggest that if you can use the
I think a large part of that is down to Swift's generic system actually quite a way from being 'feature complete'. The Swift team themselves have tried to outline a vague plan for generics, initially with the Swift Generics Manifesto, and then more recently Joe Groff's post on the Swift forums Improving the UI of Generics.
What this means in practice is that things that you expect to work intuitively, just aren't available yet. Most recently it took me a while to work out that using the new