Constraining associated types in Swift protocol - swift

I'm writing a little code snippet to learn about how associated types work, but I've come across an error I'm not sure how to interpret. The code I've written is posted below for reference.
// A basic protocol
protocol Doable {
func doSomething() -> Bool
}
// An extension that adds a method to arrays containing Doables
extension Array where Element: Doable {
func modify(using function:(Array<Doable>)->Array<Doable>) -> Array<Doable> {
return function(self)
}
}
// Another protocol with an associated type constrained to be Doable
protocol MyProtocol {
associatedtype MyType: Doable
func doers() -> Array<MyType>
func change(_:Array<MyType>) -> Array<MyType>
}
// An simple extension
extension MyProtocol {
func modifyDoers() -> Array<MyType> {
return doers().modify(using: change)
}
}
I've constrained MyType to be Doable, but the compiler complains that it cannot convert (Array<Self.MyType>) -> Array<Self.MyType> to expected argument type (Array<Doable>) -> Array<Doable>. Can anybody explain what's going on here and how I can make the compiler happy?

As the error message says, the modify function expects arguments with the type Array<Doable> and you're passing arguments with the type Array<MyType>.
The issue stems from the definition of modify, where you're explicitly using Doable in the parameters, which excludes all other types but Doable – and as associated types are not typealiases, MyType can't be converted to Doable.
The fix is to change all occurrences of Doable in the modify function to Element, as is portrayed in the Swift documentation: Extensions with a Generic Where Clause.

Related

Create a Swift extension with a where clause that filters on a struct that takes a generic

I'm trying to create an extension on Set that uses a where clause so that it only works on a struct I have that accepts a generic. But I keep running into errors about it the extension wanting the generic to be defined in the struct
In this example I'm getting the following error, with the compiler hint suggesting I use <Any>: Reference to generic type 'Test' requires arguments in <...>
struct Test<T> {
var value : T
func printIt() {
print("value")
}
}
extension Set where Element == Test {
}
However, when I use <Any> in the struct, I'm getting this error: Same-type constraint type 'Test' does not conform to required protocol 'Equatable'
extension Set where Element == Test<Any> {
}
Any suggestions on how to get the where clause to accept the Test struct for any type I'm using in the generic?
Thanks for your help
This is a limitation of Swift's type system. There's no way to talk about generic types without concrete type parameters, even when those type parameters are unrelated to the use of the type. For this particular situation (an extension for all possible type parameters), I don't believe there's any deep problem stopping this. It's a simpler version of parameterized extensions, which is a desired feature. It's just not supported (though there is an implementation in progress).
The standard way to address this today is with a protocol.
First, a little cleanup that's not related to your question (and you probably already know). Your example requires Test to be Hashable:
struct Test<T: Hashable>: Hashable {
var value : T
func printIt() {
print("value")
}
}
Make a protocol that requires whatever pieces you want for the extension, and make Test conform:
protocol PrintItable {
func printIt()
}
extension Test: PrintItable {}
And then use the protocol rather than the type:
extension Set where Element: PrintItable {
func printAll() {
for item in self { item.printIt() }
}
}
let s: Set<Test<Int>> = [Test(value: 1)]
s.printAll() // value
Just one more note on the error messages you're getting. The first error, asking you to add Any is really just complaining that Swift can't talk about unparameterized generics, and suggesting it's fallback type when it doesn't know what type to suggests: Any.
But Set<Any> isn't "any kind of Set." It's a Set where Element == Any. So Any has to be Hashable, which isn't possible. And a Set<Int> isn't a subtype of Set<Any>. There' completely different types. So the errors are a little confusing and take you down an unhelpful road.
This is not possible. The where clause requires a specific data type and simply passing a Test will not work unless I specify something more concrete like Test<String>.
Thank you to Joakim and flanker for answering the question in the comments
If you want to add extension for Set with where clause your Test must confirm to Hashable protocol.
Your Struct must look like this.
struct Test<T: Hashable> : Hashable {
var value : T
func printIt() {
print("value")
}
func hash(into hasher: inout Hasher) {
hasher.combine(value.hashValue)
}
}
So you can't use Any for your extension you must specify type that confirm to Hashable protocol.

In argument type '[ItemA]', 'ItemA' does not conform to expected type 'Sortable'

I have run into a weird error using Swift, but I can't seem to find the issue. The error should not be thrown I think, and I have verified this issue with the code below in a playground.
protocol Sortable {
}
protocol ItemA: Sortable {
}
func sortItems<T: Sortable>(items: [T]) -> [T] {
// do the sorting here
return items
}
let list: [ItemA] = []
sortItems(items: list)
You cannot pass another protocol that inherits from the constrained protocol in current Swift version (4.1).
If you make ItemA a struct, a class or an enum, it will work.
OR
If you would change your sortItems implementation to simply take Sortable as an argument like this, then you can use another protocol that inherits from Sortable, but you will lose the information about the type.
func sortItems(items: [Sortable]) -> [Sortable] {
// do the sorting here
return items
}
You can find more information on this issue here.

How can I convert between related types through a common initializer?

I'm trying to build up a family of types that can be converted to each other. For example, Float and Double can be converted to each other through their initializers. I'd like to not have to create an exhaustive list of initializers showing that each type can convert to every other type.
I tried to do something like this in a Playground, but it crashes:
protocol FloatConvertible {
init(_ x:FloatConvertible)
}
extension FloatConvertible {
init(_ x:FloatConvertible){self.init(Self(x))}
}
extension Float:FloatConvertible {}
extension Double:FloatConvertible {}
func transmute<T:FloatConvertible, U:FloatConvertible>
(a:T, b:U) -> T {
return T(b)
}
transmute(Float(3.1), b: Double(2.6))
My eventual goal isn't just to do the conversion, but to multiply a by b like so:
func *<T:FloatConvertible, U:FloatConvertible> (a:T, b:U) -> T{
return a * T(b)
}
So that I can express the multiply.
Is there a way to do this? I think part of the problem is winding up with a structure that looks like Double(Double(Double(Double(...))), but I don't think I can put a constraint that ensures T != U.
The problem is that in your init(_ x:FloatConvertible), Swift cannot infer what the concrete type of x is. It just knows that it's a FloatConvertible. Therefore when you try to do Self(x), while it can infer the concrete type of Self, it doesn't know which initialiser you want to call, meaning that it will default to your init(_ x:FloatConvertible) initialiser, thus creating an infinite loop.
If you give your custom initialiser an argument name, you'll see that Swift complains that it can't find the correct initialiser:
protocol FloatConvertible {
init(c x:FloatConvertible)
}
extension FloatConvertible {
init(c x:FloatConvertible) {
// error: missing argument name 'c:' in call
// (i.e it can't find the concrete type's initialiser)
self.init(Self(x))
}
}
A potential solution therefore is to resolve this at runtime by switching over the concrete types that x could be. However this isn't nearly as good as resolving this statically, as you can benefit from increased safety and in some cases increased performance.
In order to do this statically, you could add a generic _asOther 'shadow' function to your protocol that can convert a given floating point type to another, as well as adding the concrete type's initialisers to your protocol requirement.
This will save you from having to list out all the possible combinations of conversions – you can now just invoke _asOther from your initialiser.
protocol FloatConvertible {
init(_ other:Float)
init(_ other:Double)
init(_ other:CGFloat)
init(fromOther x:FloatConvertible)
func _asOther<T:FloatConvertible>() -> T
}
extension FloatConvertible {
init(fromOther x:FloatConvertible) {self = x._asOther()}
}
// note that we have to implement these for each extension,
// so that Swift uses the concrete types of self, preventing an infinite loop
extension Float : FloatConvertible {
func _asOther<T:FloatConvertible>() -> T {return T(self)}
}
extension Double : FloatConvertible {
func _asOther<T:FloatConvertible>() -> T {return T(self)}
}
extension CGFloat : FloatConvertible {
func _asOther<T:FloatConvertible>() -> T {return T(self)}
// note that CGFloat doesn't implement its own initialiser for this,
// so we have to implement it ourselves
init(_ other:CGFloat) {self = other}
}
func transmute<T:FloatConvertible, U:FloatConvertible>(value: T, to: U.Type) -> U {
return U(fromOther: value)
}
let f = transmute(value: CGFloat(2.6), to: Float.self)
print(type(of: f), f) // prints: Double 2.59999990463257
In the initialiser, _asOther will be called on the input value, with the type of self being inferred for the generic parameter T (in this context self is guaranteed to be a concrete type). The _asOther function will then get called on x, which will return the value as the given destination type.
Note that you don't have to use the fromOther: argument label for your custom initialiser – this will still work without any label. Although I would strongly advocate for using it to catch any problems with your code at compile time (Swift would accept code that would cause infinite loops at runtime otherwise).
Also as a side note, you should maybe re-think your design for how you want your * overload to work. It would make more sense to be returning the more precise type that you input into it (i.e Float * Double = Double) – otherwise you're just needlessly losing precision.

Swift Extensions for Collections

I'm working on a framework to make it easier to work with Key Value Observing and I've defined a protocol for converting native Swift types to NSObject as follows:
public protocol NSObjectConvertible {
func toNSObject () -> NSObject
}
Extending the builtin types was easy, simply defining the function to convert the given type to the appropriate NSObject:
extension Int8: NSObjectConvertible {
public func toNSObject () -> NSObject {
return NSNumber(char: self)
}
}
When I got to the Array type, I hit a number of snags, which I tried to work out. I didn't want to extend any array type, but only arrays whose element type was itself NSObjectConvertible. And naturally, needed Array to itself conform to the protocol.
After hunting around on SO, it looks like extending the Array type itself is a little harder because it's generic, but extending SequenceType can be done. Except that I can't both constrain the element type and declare its conformance to the protocol in the same declaration.
The following:
extension SequenceType where Generator.Element == NSObjectConvertible : NSObjectConvertible = {
public func toNSObject () -> NSObject {
return self.map() { return $0.toNSObject() }
}
}
Produces a compiler error:
Expected '{' in extension
And the carat points to the ":" where I'm trying to declare the protocol conformance. Removing the protocol conformance compiles without errors, but obviously doesn't help the case.
I'm not sure if this is a bug, or if Swift simply can't (or doesn't want to) support what I'm trying to do. Even if I simply define the extension, then try to take care of the conformance in the body, it produces the risk of passing sequences that don't really conform to what they should.
At best it's a hacky solution to just fail in cases where a sequence with non-conforming members are passed. I'd much rather let the compiler prevent it from happening.
(This is in Swift 2.1, Xcode 7.1.1)
You can't add the protocol conformance, unfortunately.

Conditionally lifting protocols to generic types in Swift

How do I say in Swift's type system "an Array<T> conforms to protocol P if the element type T conforms to protocol Q"?
I'm actually interested in a more specific version of this problem, where P and Q are the same protocol: you're saying "if the elements of the array are P-conforming, then the array is P-conforming". Here's what I have so far. (I'm trying for a simple QuickCheck library, starting from http://chris.eidhof.nl/posts/quickcheck-in-swift.html: Arbitrary marks types that can be randomly generated.)
protocol Arbitrary {
class func arbitrary() -> Self
}
extension Array {
static func arbitrary<T where T : Arbitrary>() -> [T] {
// code to create a random-length list of T objects
// using T.arbitrary() for each one
}
}
extension Array<T where T : Arbitrary> : Arbitrary {}
This fails with the error
extension of generic type 'Array' cannot add requirements
extension Array<T where T : Arbitrary> : Arbitrary {}
You can't do this in Swift, since you can't further constrain a generic type. For example, you can't add methods to Array<T> that only work when T is Comparable - that's why there are so many global functions for dealing with generic types (map, filter, sort, etc.).
From a recent Chris Lattner posts in the dev forums, it sounds like the Swift developers are headed in this direction, but it's nowhere near this yet. See if you can implement what you're trying to do as global functions that constrain T to Arbitrary:
func arbitrary<T: Arbitrary>() -> [T] {
// ..
}