Created
February 12, 2018 15:46
-
-
Save anandabits/fad4f47d35ab76067c49649ce064b089 to your computer and use it in GitHub Desktop.
A (non-working) experiment in abstracting over constraints in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NOTE: This is an experiement that does not actually work (in the 2/8/18 nightly toolchain). | |
// The idea is to use Swift's constraint inference to be able to abstract an arbitrary set of constraints. | |
/// An empty enum serving as an example of the general case of | |
/// representing a set of constraints that relate multiple types. | |
/// This specific example provides the constraint that both types are sequences and they have the same Element type. | |
enum Parallel<S1: Sequence, S2: Sequence> where S1.Element == S2.Element { | |
typealias First = S1 | |
typealias Second = S2 | |
} | |
/// A function that uses a phantom argument intended to get the compiler to infer the constraints | |
/// represented by the Parallel. | |
/// This seems to work but can produce extremely unhelpful error messages, far worse than the error messages | |
/// that would be produced if the constraints were stated explicitly here. It is also rather verbose at the usage site. | |
func foo<S1, S2>(_ s1: S1, _ s2: S2, constriants: Parallel<S1, S2>.Type = Parallel<S1, S2>.self) { | |
print(s1) | |
print(s2) | |
} | |
// compiler allows this as expected | |
foo([1, 2], [1, 2] as Set) | |
// Compiler rejects this with a useful error message: | |
// - candidate requires that the types 'Int' and 'String' be equivalent (requirement specified as 'S1.Element' == 'S2.Element' [with S1 = [Int], S2 = Set<String>]) | |
// foo([1, 2], ["1", "2"] as Set) | |
// Compiler rejects this but with a non-obvious error message: | |
// - cannot invoke 'foo' with an argument list of type '(Int, Int)' | |
// - note: expected an argument list of type '(S1, S2, constriants: Parallel<S1, S2>.Type)' | |
// foo(42, 43) | |
// Given the prior two examples it appears that the compiler infers the constraints on S1 and S2 | |
// as desired when the types both have Sequence conformances despite not meeting other constraints | |
// specified by Parallel, but only omits the default argument when they do not have the conformances. | |
/// A function that uses a where clause intended to get the compiler to infer the constraints | |
/// represented by the Parallel. Ideally this could simply be stated as Parallel<S1, S2>. | |
func bar<S1, S2>(_ s1: S1, _ s2: S2) where S1 == Parallel<S1, S2>.First { | |
print(s1) | |
print(s2) | |
} | |
foo([1, 2], [1, 2] as Set) | |
// Compiler unfortunately allows this. | |
bar([1, 2], ["1", "2"] as Set) | |
// Compiler does not allow this but fails with the same unhelpful diagnostic as in foo(42, 43) | |
//foo(42, 43) | |
/// A type that uses a where clause intended to get the compiler to infer the constraints | |
/// represented by the Parallel. Ideally this could simply be stated as Parallel<S1, S2>. | |
struct Pair<S1, S2> where S1 == Parallel<S1, S2>.First { | |
let first: S1 | |
let second: S2 | |
} | |
print(Pair(first: [1, 2], second: [1, 2] as Set)) | |
// As with bar, the compiler unfortunately allows this | |
print(Pair(first: [1, 2], second: ["1", "2"] as Set)) | |
// Again, unfortunately the compiler allows this | |
print(Pair(first: 42, second: 43)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment