extend generic Array T to adopt protocol
EDITED
The following solution is somewhat generic, conforms to protocol EuclidianPoint
, and is based upon two assumptions:
That we're allowed to include a generic type constraint for your blueprint for method
distance
in yourEuclideanPoint
protocol, and that, instead of argument type beingSelf
, we'll use a generic ([T]
). We will, however, ascertain (at compile time) that[T]
is of the same type asSelf
(and here,Self
of[Double]
,[Float]
or[Int]
type), and ascertain that [T] conforms to the protocolEuclidianPoint
.That you're ok that we leave functional programming techniques such as
.map
and.reduce
out of this specific application, and focus only on attaining a "generic array adopted to euclidian protocol". These.map
,.reduce
etc feats in Swift are indeed neat and useful, but are in many applications just wrappers for behind-the-hood for-loops, so you won't lose any performance over doing things in a manual imperative style. In fact,.reduce
is known to perform quite non-optional due to repeated array-copy-assignments while reducing the array (I won't go into this more here...). Anyway, perhaps you can make use of my example and tweak it back to something more functional-paradigmy.
We begin by a custom type protocol, MyTypes
, that will act as an interface for which types we want to include in our generic. We also add the slightly updated EuiclidianPoint
protocol, where we use protocol MyTypes
as a type restraint to the generic T
used in the distance (...)
function blue-print.
/* Used as type constraint for Generator.Element */
protocol MyTypes {
func -(lhs: Self, rhs: Self) -> Self
func +=(inout lhs: Self, rhs: Self)
}
extension Int : MyTypes { }
extension Double : MyTypes { }
extension Float : MyTypes { }
/* Extend with the types you wish to be covered by the generic ... */
/* Used as extension to Array : blueprints for extension method
to Array where Generator.Element are constrainted to MyTypes */
protocol EuclideanPoint {
func distance<T: MyTypes> (other: [T]) -> Double?
func dimension() -> UInt
}
Note that I've changed the Double
return of distance
to an optional; you may handle this as you will, but in case the lengths of self
and other
arrays differ, or types Self
and [T]
differ, there will be some need of showing non-conformance -- I'll use nil
for this here.
We can now implement implement our extension of Array
by the EuclidianPoint
protocol:
/* Array extension by EuclideanPoint protocol */
extension Array : EuclideanPoint {
func distance<T: MyTypes> (other: [T]) -> Double? {
/* [T] is Self? proceed, otherwise return nil */
if let a = self.first {
if a is T && self.count == other.count {
var mySum: Double = 0.0
for (i, sElement) in self.enumerate() {
mySum += pow(((sElement as! T) - other[i]) as! Double, 2)
}
return sqrt(mySum)
}
}
return nil
}
func dimension() -> UInt {
return UInt(self.count)
}
}
Note that in the inner if clause of the distance
function we use an explicit down cast to T
, but since we've asserted that elements of Self
are of type T
, this is ok.
Anyway, with this, we're done, and we can test our "generic" array extensions, which we note now also conforms to your protocol EuclidianPoint
.
/* Tests and Examples */
let arr1d : [Double] = [3.0, 4.0, 0.0]
let arr2d : [Double] = [-3.0, -4.0, 0.0]
let arr3d : [Double] = [-3.0, -4.0]
let arr1f : [Float] = [-3.0, -4.0, 0.0]
let arr1i = [1, 2, 3]
let _a = arr1d.dimension() // 3, OK
let _b = arr1d.distance(arr2d) // 10, OK (A->B dist)
let _c = arr1d.distance(arr1f) // nil (Incomp. types)
let _d = arr1d.distance(arr3d) // nil (Incomp. sizes)
let _e = arr1i.distance(arr1d) // nil (Incomp. types)
/* for use in function calls: generic array parameters constrained to
those that conform to protocol 'EuclidianPoint', as requested */
func bar<T: MyTypes, U: protocol<EuclideanPoint, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Double? {
// ...
return arr1.distance(Array(arr2))
/* We'll need to explicitly tell the distance function
here that we're sending an array, by initializing an
array using the Array(..) initializer */
}
let myDist = bar(arr1d, arr2d) // 10, OK
Ok!
A note still remaining from my first answer:
Extension of generic type Array to protocol was actually just recently asked here:
- Extending typed array by conforming to a protocol in Swift 2
The consensus is the you cannot do a generic extension of array to a protocol in the "neat swifty" way that you possible expect. There are however workarounds to mimic such a behaviour, one being the one I've used above. If you are interested in another method, I suggest you look into this thread.
extend generic Array T to adopt protocol
EDITED
The following solution is somewhat generic, conforms to protocol EuclidianPoint
, and is based upon two assumptions:
That we're allowed to include a generic type constraint for your blueprint for method
distance
in yourEuclideanPoint
protocol, and that, instead of argument type beingSelf
, we'll use a generic ([T]
). We will, however, ascertain (at compile time) that[T]
is of the same type asSelf
(and here,Self
of[Double]
,[Float]
or[Int]
type), and ascertain that [T] conforms to the protocolEuclidianPoint
.That you're ok that we leave functional programming techniques such as
.map
and.reduce
out of this specific application, and focus only on attaining a "generic array adopted to euclidian protocol". These.map
,.reduce
etc feats in Swift are indeed neat and useful, but are in many applications just wrappers for behind-the-hood for-loops, so you won't lose any performance over doing things in a manual imperative style. In fact,.reduce
is known to perform quite non-optional due to repeated array-copy-assignments while reducing the array (I won't go into this more here...). Anyway, perhaps you can make use of my example and tweak it back to something more functional-paradigmy.
We begin by a custom type protocol, MyTypes
, that will act as an interface for which types we want to include in our generic. We also add the slightly updated EuiclidianPoint
protocol, where we use protocol MyTypes
as a type restraint to the generic T
used in the distance (...)
function blue-print.
/* Used as type constraint for Generator.Element */
protocol MyTypes {
func -(lhs: Self, rhs: Self) -> Self
func +=(inout lhs: Self, rhs: Self)
}
extension Int : MyTypes { }
extension Double : MyTypes { }
extension Float : MyTypes { }
/* Extend with the types you wish to be covered by the generic ... */
/* Used as extension to Array : blueprints for extension method
to Array where Generator.Element are constrainted to MyTypes */
protocol EuclideanPoint {
func distance<T: MyTypes> (other: [T]) -> Double?
func dimension() -> UInt
}
Note that I've changed the Double
return of distance
to an optional; you may handle this as you will, but in case the lengths of self
and other
arrays differ, or types Self
and [T]
differ, there will be some need of showing non-conformance -- I'll use nil
for this here.
We can now implement implement our extension of Array
by the EuclidianPoint
protocol:
/* Array extension by EuclideanPoint protocol */
extension Array : EuclideanPoint {
func distance<T: MyTypes> (other: [T]) -> Double? {
/* [T] is Self? proceed, otherwise return nil */
if let a = self.first {
if a is T && self.count == other.count {
var mySum: Double = 0.0
for (i, sElement) in self.enumerate() {
mySum += pow(((sElement as! T) - other[i]) as! Double, 2)
}
return sqrt(mySum)
}
}
return nil
}
func dimension() -> UInt {
return UInt(self.count)
}
}
Note that in the inner if clause of the distance
function we use an explicit down cast to T
, but since we've asserted that elements of Self
are of type T
, this is ok.
Anyway, with this, we're done, and we can test our "generic" array extensions, which we note now also conforms to your protocol EuclidianPoint
.
/* Tests and Examples */
let arr1d : [Double] = [3.0, 4.0, 0.0]
let arr2d : [Double] = [-3.0, -4.0, 0.0]
let arr3d : [Double] = [-3.0, -4.0]
let arr1f : [Float] = [-3.0, -4.0, 0.0]
let arr1i = [1, 2, 3]
let _a = arr1d.dimension() // 3, OK
let _b = arr1d.distance(arr2d) // 10, OK (A->B dist)
let _c = arr1d.distance(arr1f) // nil (Incomp. types)
let _d = arr1d.distance(arr3d) // nil (Incomp. sizes)
let _e = arr1i.distance(arr1d) // nil (Incomp. types)
/* for use in function calls: generic array parameters constrained to
those that conform to protocol 'EuclidianPoint', as requested */
func bar<T: MyTypes, U: protocol<EuclideanPoint, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Double? {
// ...
return arr1.distance(Array(arr2))
/* We'll need to explicitly tell the distance function
here that we're sending an array, by initializing an
array using the Array(..) initializer */
}
let myDist = bar(arr1d, arr2d) // 10, OK
Ok!
A note still remaining from my first answer:
Extension of generic type Array to protocol was actually just recently asked here:
- Extending typed array by conforming to a protocol in Swift 2
The consensus is the you cannot do a generic extension of array to a protocol in the "neat swifty" way that you possible expect. There are however workarounds to mimic such a behaviour, one being the one I've used above. If you are interested in another method, I suggest you look into this thread.
Extending typed array by conforming to a protocol in Swift 2
You can't apply a lot of logic to conformance. It either does or does not conform. You can however apply a little bit of logic to extensions. The code below makes it easy to set specific implementations of conformance. Which is the important part.
This is used as a typed constraint later.
class SomeType { }
This is your protocol
protocol SomeProtocol {
func foo()
}
This is an extension of the protocol. The implementation of foo()
in an extension of SomeProtocol
creates a default.
extension SomeProtocol {
func foo() {
print("general")
}
}
Now Array
conforms to SomeProtocol
using the default implementation of foo()
. All arrays will now have foo()
as a method, which is not super elegant. But it doesn't do anything, so it's not hurting anyone.
extension Array : SomeProtocol {}
Now the cool stuff:
If we create an extension to Array
with a type constraint for Element
we can override the default implementation of foo()
extension Array where Element : SomeType {
func foo() {
print("specific")
}
}
Tests:
let arrayOfInt = [1,2,3]
arrayOfInt.foo() // prints "general"
let arrayOfSome = [SomeType()]
arrayOfSome.foo() // prints "specific"
How to extend protocol Optional, where Wrapped item is Array of Equatable generic elements?
Cristik provided a good solution. An alternative is to write an extension to an optional collection of equatable elements:
extension Optional where Wrapped: Collection, Wrapped.Element: Equatable {
func foo() { }
}
This would be applicable to arrays, array slices, and other collections.
Depending on what the extension does, you may want to use a combination of Collection
, MutableCollection
, BidirectionalCollection
, RandomAccessCollection
.
Swift: Extension on [ SomeType T ?] to produce [ T ?] possible?
I don't know if there is a simpler solution now, but you can use the same “trick” as in How can I write a function that will unwrap a generic property in swift assuming it is an optional type? and Creating an extension to filter nils from an Array in Swift, the idea goes back to this Apple Forum Thread.
First define a protocol to which all optionals conform:
protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional: Wrapped? {
return self
}
}
Now the desired extension can be defined as
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map( { $0.asOptional?.value })
}
}
and that works as expected:
let arr = [SomeType(value: 123), nil, SomeType(value: 456)]
let v = arr.values
print(v) // [Optional(123), Optional(456)]
print(type(of: v)) // Array<Optional<Int>>
Extend Array to conform to protocol if Element conforms to given protocol
Swift 4.2
In Swift 4.2 I was able to extend an array with the elements conforming to a protocol like this:
public extension Array where Element: CustomStringConvertible{
public var customDescription: String{
var description = ""
for element in self{
description += element.description + "\n"
}
return description
}
}
How to make a generic class conform to a protocol for specific type?
No, such construct isn't possible (circa Swift 3.1 at least).
For instance:
class SomeClass { }
protocol SomeProtocol { }
extension Matrix: SomeProtocol where T == SomeClass { }
Gives a very clear error message:
Extension of type
Matrix
with constraints cannot have an inheritance clause.
But all is not lost... as correctly noted by Alexander, there is already a proposal lined up for Swift 4! The feature will be called Conditional Conformances (SE-0143).
A nice example for all protocol-oriented programming hackers out there:
extension Array: Equatable where Element: Equatable {
...
}
If an array contains equatable elements than said array is also equatable.
Update. Swift 4 is out but this feature hasn’t landed yet. We may need to wait until Swift 5 for this...
How would I use an array of generics with a type parameter conforming to a protocol?
To see what's wrong with your scenario, forget about trying to declare the type of this array, and try to actually make such an array out of actual objects:
protocol MyProtocol {}
struct MyStruct<T: MyProtocol> {
let myProp: T
}
struct S1 : MyProtocol {}
struct S2 : MyProtocol {}
let myStruct1 = MyStruct(myProp: S1())
let myStruct2 = MyStruct(myProp: S2())
let array = [myStruct1, myStruct2] // error
The compiler kicks back: "heterogeneous collection literal could only be inferred to '[Any]'". And that sums it up. The types of myStruct1
and myStruct2
have nothing in common, so you cannot make an array of them.
That is why you are not able to declare the array to be of a type that will embrace both of them. There is no such type. The types of myStruct1
and myStruct2
, namely MyStruct<S1>
and MyStruct<S2>
, are unrelated.
I know that they look related, because the word "MyProtocol" in the original declaration appears to provide some sort commonality. But the word "MyProtocol" does not designate a type; it designates a constraint on the actual type, saying that whatever this one type is, it must be an adopter of MyProtocol. S1 and S2 are two different types, and so MyStruct<S1>
and MyStruct<S2>
are two different types. You can't put them together in an array. The fact that both S1 and S2 happen to adopt MyProtocol is irrelevant.
Part of the difficulty may be that you think that two generic types are somehow related because their parameterized types are related. That is not the case. The classic example is a class and its subclass:
class Cat {}
class Kitten: Cat {}
struct Animal<T: Cat> {}
let cat = Animal<Cat>()
let kitten = Animal<Kitten>()
let array2 = [cat, kitten] // error
We get the same compile error. Again, you might imagine that you can put cat
and kitten
together in an array because Kitten is a subclass of Cat. But that is not true. Animal<Cat>
and Animal<Kitten>
are unrelated types.
Extension on Array where element is a generic struct in Swift
You will need to create a protocol with an associated type:
protocol MyGenericStructProtocol {
associatedtype GenericParameter
}
Let your struct adopt the protocol, either directly, or using an extension:
extension MyStruct: MyGenericStructProtocol {
typealias GenericParameter = T
}
Now you can reference the generic type inside your extension of Array:
extension Array where Element: MyGenericStructProtocol {
func doWork() -> [Element.GenericParameter] {
return []
}
}
Check out a fully working example on this GitHub gist
Related Topics
Skaction Completion Handlers; Usage in Swift
Using Applescript with Apple Events in MACos - Script Not Working
Getting a Let Value Outside a Function
How to Set an Environment Object in Preview
Pattern Match and Conditionally Bind in a Single Switch Statement
Toggle Selection in a List - Swiftui
When Calling a Function in Swift, What Does Each Part Mean
No Such Module 'Cocoa' in Swift Playground
Add a Border with Cornerradius to an Image in Swiftui Xcode Beta 5
Mutating Function Inside Class
Realitykit - How to Set a Modelentity's Transparency
Swift 3 String Contains Exact Sentence/Word
Animating a Navigation Bar Color
Cannot Invoke 'Filter' with an Argument List of Type '((_) -> _)'
Uibarbuttonitem Doesn't Work When Created as a Property, But Does When Created in a Function