struct Test {
func isOk () -> Bool{
return true
}
var mapping: [Int: () -> Bool] = [
1: isOk
]
func test() -> Bool {
return mapping[1]()
}
}
I got this error:
Cannot convert value of type '(Test) -> () -> Bool' to expected dictionary value type '() -> Bool'
Any idea? Thanks!
You're seeing this weird type ((Test) -> () -> Bool) because Swift instance methods are curried functions.
If making isOk into a static method is acceptable, you can do this:
struct Test {
static func isOk() -> Bool { //Make it static
return true
}
var mapping: [Int : () -> Bool] = [
1 : Test.isOk // Static method must be qualified by its parent type
]
func test() -> Bool { //Bool return value necessary
//TODO: remove force unwrapping.
return mapping[1]!() //subscripting a Dict might return nil
}
}
If isOk must remain as an instance method, you can do this:
struct Test {
func isOk() -> Bool {
return true
}
var mapping: [Int : (Test) -> () -> Bool] = [ //change type
1 : isOk
]
//the instance method received from the dict needs an instance to act on
func test(instance: Test) -> Bool { //Bool return value necessary
//TODO: remove force unwrapping.
return mapping[1]!(instance)() //curried function call
}
}
#AMomchilov is dead right about why this is happening. At static scope (such as in mapping's default value assignment), instance methods are curried functions – meaning that they return a function that take a given instance to operate on as a parameter, rather than implicitly operating on self (as self doesn't refer to an instance at static scope).
Another potential solution to the ones already proposed if you don't wish to make isOk a static method and would like mapping to remain a [Int : () -> Bool], is to make it lazy. This will allow you to access isOk in the assignment of mapping, as it's now at instance scope – therefore meaning the function type will be () -> Bool.
struct Test {
func isOk () -> Bool {
return true
}
lazy var mapping: [Int: () -> Bool] = [
1 : self.isOk
]
mutating func test() -> Bool {
// you need to think about situations where mapping[1] returns nil
// and deal with it in a more proper way than force unwrapping
return mapping[1]!()
}
}
The only caveat to this approach is that you'll need to mark test() as mutating – as the default value of a lazy variable will be set upon accessing it for the first time, thus mutating the struct.
Although a maybe even cleaner alternative (depending on the use case), is just to assign your function to the dictionary (or the dictionary itself) in the initialiser. This will allow you to keep mapping non-lazy, therefore meaning test doesn't have to be mutating.
struct Test {
func isOk () -> Bool {
return true
}
init() {
mapping[1] = isOk
}
var mapping = [Int: () -> Bool]()
func test() -> Bool {
// you need to think about situations where mapping[1] returns nil
// and deal with it in a more proper way than force unwrapping
return mapping[1]!()
}
}
Again, this ensures that you're at instance scope when assigning the method to the dictionary, thus ensuring that it has the signature () -> Bool.
Related
Here's my code snippet:
open class SomeClass {
let driver: Driver<Bool>
init(input: Observable<String>) {
driver = input
.map( { s -> Bool in self.convert(text: s) }) // error 'self' captured by a closure before all members were initialized
.asDriver(onErrorJustReturn: false)
}
func convert(text: String) -> Bool {
// Do some complex calculation
return true
}
}
Explanation: In my SomeClass, I have a driver object of type Driver<Bool>, then inside my init, I'll take an Observable<String> and map it to a Observable<Bool>.
However in order to do the conversion I need to call the func convert() inside the mapping closure, thus I'm getting the error
'self' captured by a closure before all members were initialized
Can someone please show me how to get through this?
The simplest solution is to move convert out of the class:
open class SomeClass {
let driver: Driver<Bool>
init(input: Observable<String>) {
driver = input
.map( { s -> Bool in convert(text: s) }) // error 'self' captured by a closure before all members were initialized
.asDriver(onErrorJustReturn: false)
}
}
func convert(text: String) -> Bool {
// Do some complex calculation
return true
}
You should probably make convert(text:) private to avoid name pollution.
Below, there are one protocol which has two closure variables and a functions. And there is also a class conforming to the protocol.
import Foundation
protocol TestProtocol: class {
var firstClosure: () -> () { get set }
var secondClosure: () -> () { get set }
func testFunction()
}
extension TestProtocol {
func testFunction() {
_ = firstClosure
_ = secondClosure
}
}
class A: TestProtocol {
var firstClosure: () -> ()
var secondClosure: () -> ()
var number = 0
init() {
print(number)
firstClosure = {
self.number = 1
}()
print(number)
secondClosure = {
self.number = 2
}()
print(number)
testFunction()
print(number)
}
}
let a = A()
My expected result is 0, 0, 0, 2. But, when I define closure variable, the closure variable execute immediately. What should I do?
The issue is that you aren't defining closures, you're defining Voids due to the fact that you execute the closures right after defining them by writing () after the closure. The type of firstClosure and secondClosure are both Void, which is a typealias for (). If you wanted to declare a closure that takes no input arguments and returns no value (or rather returns Void), you need to declare it as () -> ().
protocol TestProtocol: class {
var firstClosure: () -> () { get set }
var secondClosure: () -> () { get set }
func testFunction()
}
extension TestProtocol {
func testFunction() {
firstClosure()
secondClosure()
}
}
You should also modify your conforming class to directly set the closure's instead of setting them in the initializer and not to call a function with side effects (testFunction) in the initializer, but rather call it when you actually need it after initialisation. You need to declare your closures as lazy, since they access self.
class A: TestProtocol {
var number = 0
lazy var firstClosure: () -> Void = {
self.number = 1
}
lazy var secondClosure: () -> Void = {
self.number = 2
}
}
A().testFunction()
I'm trying to make a type erasure around Range and ClosedRange but I'm being stuck because they have some methods which take Self as parameter.
All types erasure samples found on the internet don't deal with that case.
Am I trying to do something impossible ?
Here's my implementation (simplified) :
protocol RangeType {
associatedtype _Bound: Comparable
func overlaps(_ other: Self) -> Bool
}
struct AnyRange<Bound: Comparable>: RangeType {
typealias _Bound = Bound
private let _overlaps: (AnyRange<Bound>) -> Bool
init<R: RangeType>(_ range: R) where R._Bound == Bound {
// Cannot assign value of type (R) -> Bool to type (AnyRange<...>) -> Bool
self._overlaps = range.overlaps
}
func overlaps(_ other: AnyRange<Bound>) -> Bool {
return _overlaps(other)
}
}
extension Range: RangeType {
typealias _Bound = Bound
}
extension ClosedRange: RangeType {
typealias _Bound = Bound
}
Before I propose my solution to the problem, first note that what you are trying to do may not be defined. The protocol RangeType ensures that overlaps(_:) is defined for instances whose type is the same as the type that is implementing the function. The type erasure you are attempting to get by mimicking that of AnyIterator cannot be achieved this way because while AnyRange may guarantee that the bounds are identical, the actual underlying types themselves may not be (a requirement of the protocol).
However, that is solved here. If you wish, you may add a special case to handle comparisons between two different types (although evaluation to false as is done here is perhaps desirable)
protocol RangeType {
associatedtype _Bound: Comparable
func overlaps(_ other: Self) -> Bool
}
struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
var range: Range
}
struct AnyRange<Bound: Comparable>: RangeType {
// MARK: RangeType
typealias _Bound = Bound
// Calls the closure of the `_overlaps` property which shields each type and allows self to be passed through
func overlaps(_ other: AnyRange<Bound>) -> Bool { _overlaps.closure(other, self) }
// Shielding structure. Allows us to compare to `AnyRange` instances
private struct OverlapContainer<A, B> {
private(set) var closure: (A, B) -> Bool
init(closure: #escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
// Holds reference to the actual range type. Note that if this is a class type, a strong reference will be created
private let range: Any
/**
Represents this particular type. Should not be called elsewhere in the structure as the cast would fail if `RT != R`
passed to the initiliazer
NOTE: `RT` as the generic type is used instead of `R` to keep us aware of this fact
*/
private nonmutating func rangeComparison<RT: RangeType>() -> RangeComparison<RT, Bound> { RangeComparison<RT, Bound>(range: range as! RT) }
init<R: RangeType>(_ range: R) where R._Bound == Bound {
self.range = range
self._overlaps = .init { other, this in
let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
// If the two types are the same, the comparison can be made
if type(of: other.range).self == R.self {
let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
return thisComparison == otherComparison
}
else { print("Not the same type"); return false } // Otherwise the comparison is invalid
}
}
}
extension Range: RangeType {
typealias _Bound = Bound
}
extension ClosedRange: RangeType {
typealias _Bound = Bound
}
// Examples
let range: Range<Int> = .init(5...8)
let rangeII: ClosedRange<Int> = 1...6
let any: AnyRange<Int> = .init(range)
let anyII: AnyRange<Int> = .init(rangeII)
print(any.overlaps(anyII)) // false.` Range` is not the same type as `ClosedRange`
let rangeIII: ClosedRange<Double> = 3.0...5.5
let rangeIV: ClosedRange<Double> = 1.0...4.0
let anyIII: AnyRange<Double> = .init(rangeIII)
let anyIV: AnyRange<Double> = .init(rangeIV)
print(anyIII.overlaps(anyIV)) // true. Both are 'ClosedRange<Double>' and actually overlap one another
There is a lot here, so let me explain each piece
struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
var range: Range
}
This structure is what is used to represent a given AnyRange type. As I mentioned, comparing any two RangeType instances is not defined if they are not of the same type. This provides a medium to ensure that this is the case as well as making it convenient to equate two AnyRange types through this structure.
The rangeComparison<RT: RangeType>() method uses the type of the RangeType (R) passed into the initializer and casts the range property (set as Any and assigned to the instance passed to the initializer) to this type to create a RangeComparison instance. The range property is what keeps hold of the actual underlying type.
private struct OverlapContainer<A, B> {
private(set) var closure: (A, B) -> Bool
init(closure: #escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
This structure actually allows us to (indirectly) make comparisons between two AnyRange instances through the overlaps(_:) method of AnyRange and a closure. We simply call the _overlaps property's closure property, supplying the other AnyRange instance and a copy of this instance. A copy is used to ensure that the closure can use self without having to use self , as the compiler will complain that "Escaping closure captures mutating self parameter" (hence the reason that OverlapContainer has two generic types).
init<R: RangeType>(_ range: R) where R._Bound == Bound {
self.range = range
self._overlaps = .init { other, this in
let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
// If the two types are the same, the comparison can be made
if type(of: other.range).self == R.self {
let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
return thisComparison == otherComparison
}
else { print("Not the same type"); return false } // Otherwise the comparison is invalid
}
}
Finally, we check if the two comparisons have the same type. If you try to specify each return type as RangeComparison<R, Bound>, it will compile but will crash if the types of each comparison's range property are not the same as the type R inferred from the generic initializer. You also "Cannot explicitly specialize a generic function", and so must specify a type for the result of rangeComparison(). For these two reasons, we check the type and then check if they overlap.
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 })
}
}
Error: Cannot convert the expression type (String, MyType) to ()
From the following code
Test(method: {[weak self] (message: String) in self?.callback(message)}, instance: self)
and if I add a return statement, it works, and the error goes away
Test(method: {[weak self] (message: String) in self?.callback(message); return}, instance: self)
Not sure how to handle the above without having to have the dummy return statement, any advise.
Here's my class Test
public class Test {
private var instance: AnyObject?
private var method: ((message: String) -> ())?
public init(method: (String -> ())?, instance: AnyObject) {
}
}
Edit
I've done a playground based minimalistic example (please copy paste for a test)
class Test {
private var _method: ((String) -> ())?
weak private var _instance: AnyObject?
init(method: (String -> ())?, instance: AnyObject?) {
_method = method
_instance = instance
}
}
class Another {
func register() {
//this doesn't need a return
Test(method: {(message: String) in self.callback(message)}, instance: self)
//this needs a return once I add [weak self]
Test(method: { [weak self] (message: String) in self?.callback(message); return}, instance: self)
}
func callback(message: String) {
println(message)
}
}
Not sure how to handle the above without having to have the dummy return statement, any advise.
You have solved the problem beautifully. Anonymous functions automatically use a one-line function body as a return value, so to prevent that from causing a type mismatch with the expected return type (Void) you have to add another line of code so that it is not a one-line function body. The dummy return statement, which itself returns Void, is a great way to handle it; I would just use that and move on. There are some snazzier workarounds but what you have is precisely what I would do.
EDIT: To understand the source of the type mismatch, try this:
struct Test {
func voider() -> Void {}
}
let testMaybe = Optional(Test())
let result = testMaybe?.voider()
Now result is not a Void; it's an Optional wrapping a Void. That is what's happening to you; a Void is expected but your one-line anonymous function returns an Optional wrapping a Void. By adding another line that returns Void explicitly, you solved the problem.
The implicit return is returning the result of your callback() method. That return value conflicts with the closure's return value of void. You thus need an explicit, if ugly, return.