Swift 4 extension function on generic protocol which takes a generic function as parameter - swift

swift --version
Swift version 4.1 (swift-4.1-RELEASE)
Target: x86_64-unknown-linux-gnu
Given a simple protocol which defines a generic producer (Source):
protocol Source {
associatedtype T
func produce() -> T
}
And a mapping capable of converting between source types:
struct Mapping<T, U : Source> : Source {
typealias Transformation = (U.T) -> T
private let upstream : U
private let block : Transformation
init(_ upstream: U, _ block : #escaping Transformation) {
self.upstream = upstream
self.block = block
}
func produce() -> T {
return block(upstream.produce())
}
}
And a sample source which produces static text:
struct TextSource : Source {
private let text : String
init(_ text: String) {
self.text = text
}
func produce() -> String {
return text
}
}
I can use this to, e.g., count characters...
let t = TextSource("Hi!")
let f = Mapping(t, { (text: String) -> Int in
return text.count
})
print(f.produce()) // output: 3
But I'd rather like to use a generic map extension function on Source such that transformations can be chained, e.g.:
let t = TextSource("Hi!").map { (text: String) -> Int in
return text.count
}
Approach A
extension Source {
func map<T, U : Source>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
return Mapping(self, block)
}
}
That is rejected by the swift compiler:
error: generic parameter 'U' is not used in function signature
func map<T, U : Source>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
^
Approach B
extension Source {
func map<T>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
return Mapping(self, block)
}
}
In this case the compiler complains about the missing type parameter:
error: use of undeclared type 'U'
func map<T>(_ block: #escaping Mapping<T, U>.Transformation) -> Source {
^
Question
Which type parameters and constraints need to be specified on the map extension function in order to satisfy the compiler?

You cannot use Source as a concrete return type for map because it is a protocol with an associated type requirement.
To solve this, you can have the map function return Mapping<X, Self>:
extension Source {
func map<Result>(_ transform: #escaping (T) -> Result) -> Mapping<Result, Self> {
return Mapping(self, transform)
}
}
The function now has a Self requirement. The resulting Mapping type has a generic type parameter Self that is replaced by the concrete implementation of Source, e.g. Mapping or TextSource.

Related

How do I write the not/negate higher order function in swift?

I am a Javascripter and I love using the not/negate function:
function not (predicateFunc) {
return function () {
return !predicateFunc.apply(this, arguments);
};
}
I am trying to do the same thing with swift:
func not <A> (_ f: #escaping (_ A: Any) -> Bool) -> (A) -> Bool {
return { a in !f(a) }
}
But I am getting errors like
generic parameter 'T' could not be inferred
and
Cannot convert value of type '(_) -> Bool' to expected argument type '(Any) -> Bool'
The outcome I am looking for is when I have a function like this:
func isEmpty<T: Collection>(collection: T) -> Bool {
return collection.count == 0
}
I can just create a notEmpty function like this:
let notEmpty = not(isEmpty)
And then use it like
notEmpty([3,4,5]) // true
What am I doing wrong?
Using Any is a code smell. You can just extend Collection directly:
extension Collection {
var notEmpty: Bool {
return !isEmpty
}
}
[1, 3, 5].notEmpty // true
Your functional definition of not can work like this:
func not <A> (_ f: #escaping (_ a: A) -> Bool) -> (A) -> Bool {
return { a in !f(a) }
}
But to call it you would need something like this:
let arrayNotEmpty = not { (array: [Int]) in array.isEmpty }
arrayNotEmpty([1, 3, 5]) // true
You have two errors:
You're using A as both the type parameter and as the argument name.
You're using Any as the argument type instead of using the type parameter (A) as the argument type.
Try this:
func not<A>(predicate: #escaping (A) -> Bool) -> (A) -> Bool {
return { !predicate($0) }
}
Note that in this version, I'm not using argument names for the predicate argument. You don't need an argument name in the declaration ((A) -> Bool) and I'm using the anonymous argument name ($0) in the body.
Okay, so you want to write this:
func isEmpty<T: Collection>(collection: T) -> Bool {
return collection.count == 0
}
func not<A>(_ predicate: #escaping (A) -> Bool) -> (A) -> Bool {
return { !predicate($0) }
}
let notEmpty = not(isEmpty)
And you get this error:
let notEmpty = not(isEmpty)
^ Generic parameter 'A' could not be inferred
The problem is that this code tries to create a generic closure, but Swift doesn't support generic closures.
That is to say, what would the type of nonEmpty be? It would be something like:
<A: Collection>(A) -> Bool
and Swift doesn't support that.

Cannot convert value of type '(T) -> Void'

Example:
struct Wrapper<T> {
var key: Int = 0
var listeners: [Int: (T) -> Void] = Dictionary()
mutating func add(_ handler:#escaping (T) -> Void) {
self.key += 1
self.listeners[self.key] = handler
}
func get(key: Int) -> (T) -> Void {
return self.listeners[key]!
}
}
Test protocol:
protocol CommonProtocol {
}
Class that create Wrapper of test class
class C {
var wrapper: Wrapper = Wrapper<CommonProtocol>()
func add<T: CommonProtocol>(_ handler: #escaping (T) -> Void) {
self.wrapper.add(handler) //Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
}
}
Image with error
I get error:
Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
Question:
Why (T) -> Void can't be casted to (CommonProtocol) -> Void ? The T
is explicitly declared as <T: CommonProtocol>
This is my first question, if you have some suggestions please don't hesitate to contact me
You don't need to make func add generic.
When you specify in func add<T: CommonProtocol>... you explicitly telling the compiler that your function accepts all Types that inherit CommonProtocol but your Wrapper specifies that accepts CommonProtocol not inherited types.
Solution
Either type-erase class C:
Class C<T: CommonProtocol> {
var wrapper: Wrapper<T>
....
}
or if type T doesn't actually matter to you then:
func add(_ handler: #escaping (CommonProtocol) -> Void)
but second one doesn't make sense at all. You have to downcast it every-time you'll use this method (and downcasts are very bad :D)
Note: It's actually not related to this question, but one of your options is to type-erase the CommonProtocol too.

How to pass closure with argument as argument and execute it?

Initializer of class A takes an optional closure as argument:
class A {
var closure: ()?
init(closure: closure()?) {
self.closure = closure
self.closure()
}
}
I want to pass a function with an argument as the closure:
class B {
let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'
func action(_ i: Int) {
//...
}
}
Class A should execute the closure action with argument i.
I am not sure about how to write this correctly, see error in code comment above. What has to be changed?
Please make your "what-you-have-now" code error free.
Assuming your class A like this:
class A {
typealias ClosureType = ()->Void
var closure: ClosureType?
init(closure: ClosureType?) {
self.closure = closure
//`closure` would be used later.
}
//To use the closure in class A
func someMethod() {
//call the closure
self.closure?()
}
}
With A given above, you need to rewrite your class B as:
class B {
private(set) var a: A!
init() {
//initialize all instance properties till here
a = A(closure: {[weak self] in self?.action(1)})
}
func action(i: Int) {
//...
}
}
The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.
If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.
var closure: (Int) -> Void
Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.
init(closure: (Int) -> Void) {
Types as Parameters
In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.
So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.
func swallowInt(number: Int) {}
Compound Types
Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.
let httpStatusCode: (Int, String) = (404, "Not Found")
A function could easily take a tuple as its argument:
func swallowStatusCode(statusCode: (Int, String)) {}
Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.
Function Types As Parameters
So we can use these concepts to re-write function A:
class A {
var closure: (() -> Void)?
init(closure: (() -> Void)?) {
self.closure = closure
self.closure()
}
}
Passing an argument would then just be:
func foo(closure: (Int) -> Void) {
// Execute the closure
closure(1)
}
One way to do what I think you're attempting is with the following code:
class ViewController: UIViewController {
override func viewDidLoad() {
let _ = A.init(){Void in self.action(2)}
}
func action(i: Int) {
print(i)
}
}
class A: NSObject {
var closure : ()?
init(closure: (()->Void)? = nil) {
// Notice how this is executed before the closure
print("1")
// Make sure closure isn't nil
self.closure = closure?()
}
}

Declare a function in Swift that returns a closure with a generic T.Type

we are trying to make a function in Swift , that returns a closure. The return type is as follows:
func completionBlock() -> (Response<T, NSError>) -> ()
where Response itself is a struct with 2 generics. The second will be always NSError, but the first depends on a switch.
Declaration of Response:
public struct Response<Value, Error: ErrorType> { ...
What we want to achieve is a function that will return a closure with a type that depends on a value of variable.
Depending on this value, and with a switch, it specifies one type or another, the only requirement for the generic parameter is that it must conform to a specific protocol, but we can't find a solution.
What we got so far is:
class BaseClass {
var endpoint: Int
...
func completionBlock() -> (Response<T, NSError>) -> () {
switch endpoint
{
case 1:
return getHandleResponseClosure(Car.self)
case 2:
return getHandleResponseClosure(Truck.self)
}
}
func getHandleResponseClosure<T: Mappable>(_: T.Type) -> (Response<T, NSError>) -> () {
let closure = { (_: Response<T, NSError>) -> () in
// ...
}
return closure
}
}
But this don't compile: "Use of undeclared Type T" at func completionBlock(), the type at this point is generic, it only requires to conform to protocol Mappable, but we don't know how to specify this.
Thanks!!, Greetings
While there are other problems with your code, the fundamental one is that generic types in method signatures must be declared directly after the name of the method. Take a look at completionBlock below. This code compiles and works.
struct Response<T, E: Error> {
}
struct Car {}
struct Truck {}
class BaseClass {
func completionBlock<T>() -> ((Response<T, NSError>) -> ()) {
return { response in /* What goes here? */ }
}
}
let c = BaseClass()
let r: (Response<Car, NSError>) -> () = c.completionBlock()
On a side note, I would replace struct Response<T, E: Error> with…
enum Response<T> {
case success(T)
case error(Error)
}

Swift compiler is unable to resolve recursive use of generics

I am trying to implement the chain of responsibility pattern in Swift.
public class Chain<T, U> {
private var command: (T?, (U?) -> Void) -> Void
private var runCommand: (() -> Void)?
private var nextCommand: ((U?) -> Void)?
private init(command: (T?, (U?) -> Void) -> Void) {
self.command = command
}
private func next(u: U?) {
self.nextCommand?(u)
}
func then<V>(command: (U?, (V?) -> Void) -> Void) -> Chain<U, V> {
let c = Chain<U, V>(command: command)
self.nextCommand = { command($0, c.next) }
c.runCommand = self.runCommand
return c
}
func endWith(command: (U?) -> Void) {
self.nextCommand = command
self.runCommand!()
}
static func build<V>(command: ((V?) -> Void) -> Void) -> Chain<AnyObject, V> {
let c = Chain<AnyObject, V>(command: { _, next in command(next) })
c.runCommand = { command(c.next) }
return c
}
}
My class does not raise any compilation error but a simple use case (such as the one below) does not work. It raises the following error: error: cannot invoke 'endWith' with an argument list of type '((_?) -> ()) ; expected an argument list of type '((U?) -> Void)'
Any thought?
Chain.build { next in
print("Foo")
next("Bar")
}
.then { o, next in
print(o)
next(15)
}
.endWith { o in
print(o)
}
I know it is an edge case of generics usage in Swift. However, as it is not possible to explicitly specialize a generic type, I have not found any solution so far.
The compiler isn't able to infer the types in your example. You just need to specify them where they're ambiguous:
Chain<String,Int>.build { next in
print("Foo")
next("Bar")
}
.then { (o: String?, next: Int? -> Void) in
print(o)
next(15)
}
.endWith { o in
print(o)
}