I am using Swift 4 and looking for a way to create extension function for array collection with arguments of type
typealias Listener<T> = (T) -> Void
however extension below cannot be created (Use of undeclared type 'T')
extension Sequence where Element == Listener<T>{
func callAll(t: T){
self.forEach { $0(t) }
}
}
Is there a way to make it work?
You cannot introduce new generic parameters at the header of an extension like T in your code, but each method can have generic parameters.
typealias Listener<T> = (T) -> Void
extension Sequence {
func callAll<T>(t: T)
where Element == Listener<T>
{
self.forEach { $0(t) }
}
}
let listeners: [Listener<Int>] = [
{ print($0) },
{ print($0 * 2) },
]
listeners.callAll(t: 2)
Related
I wanted to create a "where_non_null" operation that works on any swift sequence - which is easy if you return an array, but obviously that is potentially bad performance wise - because you are forcing the entire sequence to resolve in memory - so I created the following that just goes line by line:
//
// this iterates through the underlying sequence, and returns only the values that are not null
//
public class Not_null_iterator<T> : IteratorProtocol
{
public typealias Element = T
private let next_function : () -> T?
init<T_iterator: IteratorProtocol>( _ source: T_iterator ) where T_iterator.Element == Optional<T>
{
var iterator = source
next_function =
{
while (true)
{
if let next_value = iterator.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
else
{
return nil
}
}
}
}
public func next() -> T? {
next_function()
}
}
//
// a sequence wrapping an underlying sequence, that removes any nulls as we go through
//
public class Not_null_sequence<T > : Sequence
{
private var iterator_creator : () -> Not_null_iterator<T>
init<T_source_sequence : Sequence >( _ source : T_source_sequence ) where T_source_sequence.Element == Optional<T>
{
iterator_creator =
{
Not_null_iterator(source.makeIterator())
}
}
public func makeIterator() -> Not_null_iterator<T>
{
iterator_creator()
}
}
extension Sequence
{
//
// return only the not null values in the sequence without ever resolving more than one item in memory at one time and remove the optionality on the type
//
func where_not_null<T>() -> Not_null_sequence<T> where Element == Optional<T>
{
return Not_null_sequence( self)
}
}
class Where_not_null_tests : XCTestCase
{
public func test_where_not_null()
{
let source = [1, 2, 3, nil, 4]
let checked : [Int] = Array(source.where_not_null())
XCTAssertEqual([1,2,3,4],checked)
}
}
which works great - however I had to define the next() and make_iterator() functions in the constructor, because I couldn't find any type safe way of putting the source into a class level variable.
Is there a way of doing that?
[and yes, I'm aware swift people prefer camel case]
Rather than just using one generic parameter, you'd need two generic parameters. You can't just constrain one generic parameter to say that it has to be some sequence with an element of some Optional. You need another generic parameter to say what the optional's type is:
class NotNilIterator<T: IteratorProtocol, U>: IteratorProtocol where T.Element == U? {
typealias Element = U
var iterator: T
init(_ source: T) {
iterator = source
}
func next() -> Element? {
// I feel this is clearer what is going on
while true {
switch iterator.next() {
case .some(.none):
continue
case .none:
return nil
case .some(.some(let element)):
return element
}
}
}
}
class NotNilSequence<T: Sequence, U> : Sequence where T.Element == U?
{
let sequence: T
init(_ source : T)
{
sequence = source
}
public func makeIterator() -> NotNilIterator<T.Iterator, U>
{
.init(sequence.makeIterator())
}
}
whereNotNil would then be declared like this:
func whereNotNil<T>() -> NotNilSequence<Self, T> where Self.Element == T?
{
return .init(self)
}
Note the use of self types. The first parameter is the type of the underlying sequence, the second is the non-optional type.
Note that this sort of "lazily computed sequence" is already built into Swift. To lazily filter out the nils, do:
let array = [1, 2, 3, nil, 4]
let arrayWithoutNil = array.lazy.compactMap { $0 }
The downside is that the type names are quite long. arrayWithoutNil is of type
LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[Int?]>.Elements, Int?>>, Int>
But you can indeed get non-optional Ints out of it, so it does work.
The way swift generics work can sometimes be very confusing (but has it's advantages). Instead of declaring that a variable is of a generic protocol (resp. a protocol with associated types), you instead declare another generic type which itself conforms to your protocol. Here's your iterator as an example (I have taken the liberty to clean up the code a bit):
public class Not_null_iterator<T, T_iterator> : IteratorProtocol where
T_iterator: IteratorProtocol,
T_iterator.Element == Optional<T>
{
private var source: T_iterator
init(_ source: T_iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
The non-null sequence works analogous:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Not_null_iterator<T, Source.Iterator> {
Not_null_iterator(self.source.makeIterator())
}
}
Using this some IteratorProtocol is just a nice way to let the compiler figure out the type. It is equivalent to saying Not_null_iterator<T, Source.Iterator>
As a (potentially) interesting side-note, to clean up the generic mess even more, you can nest the iterator class inside the Not_null_sequence:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Iterator{
Iterator(self.source.makeIterator())
}
public class Iterator: IteratorProtocol {
private var source: Source.Iterator
init(_ source: Source.Iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
}
Here's something I'm playing with. The problem is that I have a container class that has a generic argument which defines the type returned from a closure. I want to add a function that is only available if they generic type is optional and have that function return a instance containing a nil.
Here's the code I'm currently playing with (which won't compile):
open class Result<T>: Resolvable {
private let valueFactory: () -> T
fileprivate init(valueFactory: #escaping () -> T) {
self.valueFactory = valueFactory
}
func resolve() -> T {
return valueFactory()
}
}
public protocol OptionalType {}
extension Optional: OptionalType {}
public extension Result where T: OptionalType {
public static var `nil`: Result<T> {
return Result<T> { nil } // error: expression type 'Result<T>' is ambiguous without more context
}
}
Which I'd like to use like this:
let x: Result<Int?> = .nil
XCTAssertNil(x.resolve())
Any idea how to make this work?
I don't think you can achieve this with a static property, however you can achieve it with a static function:
extension Result {
static func `nil`<U>() -> Result where T == U? {
return .init { nil }
}
}
let x: Result<Int?> = .nil()
Functions are way more powerful than properties when it comes to generics.
Update After some consideration, you can have the static property, you only need to add an associated type to OptionalType, so that you'd know what kind of optional to have for the generic argument:
protocol OptionalType {
associatedtype Wrapped
}
extension Optional: OptionalType { }
extension Result where T: OptionalType {
static var `nil`: Result<T.Wrapped?> {
return Result<T.Wrapped?> { nil }
}
}
let x: Result<Int?> = .nil
One small downside is that theoretically it enables any kind of type to add conformance to OptionalType.
Lets say I want to have an array of functions (closures) that I will later want to dispatch. I want all the closures to take an Array of any type as a single parameter.
I tried:
var closureList: [(Array) -> Void]
This gives a compiler error: reference to generic type 'Array' requires arguments in <...>
I don't want to store closure of a certain type of Array but any type of Array so I tried this:
protocol GeneralArray {
}
extension Array: GeneralArray {
}
var closureList: [(GeneralArray) -> Void]
This compiles but when I try to append a closure:
func intArrayFunc([Int]) -> Void {
}
closureList.append(intArrayFunc)
I get a compiler error: cannot invoke 'append' with an argument list of type '(([Int]) -> Void)'.
Is there a way in swift to store closures that take different types as parameters in an array?
Using GeneralArray is in this case almost the same as using [Any] instead. Therefore a function of type [Int] -> Void is not convertible to such a type.
But in order to have a general way to handle any array a GeneralArray would probably have a property of type [Any]:
protocol GeneralArray {
var anyArray: [Any] { get }
}
extension Array: GeneralArray {
var anyArray: [Any] {
return self.map{ $0 as Any }
}
}
var closureList: [(GeneralArray) -> Void] = []
func intArrayFunc([Int]) -> Void {
}
So you have to wrap the function in a closure which is of type (GeneralArray) -> Void :
// wrapping the function in an appropriate closure
closureList.append({ array in intArrayFunc(array.anyArray.map{ $0 as! Int }) })
// with a helper function
closureList.append(functionConverter(intArrayFunc))
There are two possible helper functions which can "convert" the function:
func functionConverter<T>(f: [T] -> Void) -> GeneralArray -> Void {
return { array in
// "safe" conversion to an array of type [T]
let arr: [T] = array.anyArray.flatMap{
if let value = $0 as? T {
return [value]
}
return []
}
f(arr)
}
}
func unsafeFunctionConverter<T>(f: [T] -> Void) -> GeneralArray -> Void {
return { array in
f(array.anyArray.map{ $0 as! T })
}
}
I want to specify a protocol that manages some type objects that conform to another protocol. Like this:
// Specify protocol
protocol ElementGenerator {
func getElements() -> [Element]
}
protocol Element {
// ...
}
// Implement
class FooElementGenerator: ElementGenerator {
func getElements() -> [FooElement] {
// Generate elements here
return [FooElement()]
}
}
class FooElement {
// ...
}
When trying to compile this, I get an error:
Type 'FooElementGenerator' does not conform to protocol 'ElementGenerator'
hinting that candidate func getElements() -> [FooElement] has non-matching type of () -> [FooElement], but instead it expects () -> [Element].
How this kind of an error can be fixed?
UPDATE:
This solution seems to be working:
protocol ElementGenerator {
typealias T:Element
func getElements() -> [T]
}
protocol Element {
// ...
}
class FooElementGenerator: ElementGenerator {
typealias T = FooElement
func getElements() -> [T] {
return [T()]
}
}
class FooElement: Element {
// ...
}
But when I try to create a variable like this:
let a: ElementGenerator = FooElementGenerator()
a new error appears:
Protocol 'ElementGenerator' can only be used as a generic constraint because it has Self or associated type requirements
When implementing protocol methods, the return type must be same but you may return child class object like this;
protocol ElementGenerator {
func getElements() -> [Element]
}
//#objc for bridging in objective C
#objc protocol Element {
// ...
}
// Implement
class FooElementGenerator: NSObject,ElementGenerator {
override init() {
super.init();
//--
let fooElements:[FooElement] = self.getElements() as! [FooElement]
}
func getElements() -> [Element] {
// Generate elements here
return [FooElement()]
}
}
class FooElement:NSObject, Element {
// ...
override init() {
super.init();
//--
NSLog("FooElement init");
}
}
The error message in the second case is given since you have defined ElementGenerator with an “Associated Type”, and this means that you can only use it in giving constraints for types.
For instance, if you need to have a function defined for generic ElementGenerator values, you could write something like this:
func f<T1:ElementGenerator>(elemGenerator:T1) -> Element {
return elemGenerator.getElements()[0]
}
var a : Element = FooElementGenerator()
var b : Element = BarElementGenerator()
var x : Element = f(a)
var y : Element = f(b)
var z : FooElement = f(a) as! FooElement
As of Swift 2.0 it seems we can get closer to extensions of generic types applicable to predicated situations.
Although we still can't do this:
protocol Idable {
var id : String { get }
}
extension Array where T : Idable {
...
}
...we can now do this:
extension Array {
func filterWithId<T where T : Idable>(id : String) -> [T] {
...
}
}
...and Swift grammatically accepts it. However, for the life of me I cannot figure out how to make the compiler happy when I fill in the contents of the example function. Suppose I were to be as explicit as possible:
extension Array {
func filterWithId<T where T : Idable>(id : String) -> [T] {
return self.filter { (item : T) -> Bool in
return item.id == id
}
}
}
...the compiler will not accept the closure provided to filter, complaining
Cannot invoke 'filter' with an argument list of type '((T) -> Bool)'
Similar if item is specified as Idable. Anyone had any luck here?
extension Array {
func filterWithId<T where T : Idable>(id : String) -> [T] {
...
}
}
defines a generic method filterWithId() where the generic
placeholder T is restricted to be Idable. But that definition introduces a local placeholder T
which is completely unrelated to the array element type T
(and hides that in the scope of the method).
So you have not specified that the array elements must conform
to Idable, and that is the reason why you cannot call
self.filter() { ... } with a closure which expects the elements
to be Idable.
As of Swift 2 / Xcode 7 beta 2, you can define extension methods on a generic type which are more restrictive on the template
(compare Array extension to remove object by value for a very similar issue):
extension Array where Element : Idable {
func filterWithId(id : String) -> [Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}
Alternatively, you can define a protocol extension method:
extension SequenceType where Generator.Element : Idable {
func filterWithId(id : String) -> [Generator.Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}
Then filterWithId() is available to all types conforming
to SequenceType (in particular to Array) if the sequence element
type conforms to Idable.
In Swift 3 this would be
extension Sequence where Iterator.Element : Idable {
func filterWithId(id : String) -> [Iterator.Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}