How Does Typecasting/Polymorphism Work with This Nested, Closure Type in Swift

How does typecasting/polymorphism work with this nested, closure type in Swift?

It's all about variance and Swift closures.

Swift is covariant in respect to closure return type, and contra-variant in respect to its arguments. This makes closures having the same return type or a more specific one, and same arguments or less specific, to be compatible.

Thus (Arg1) -> Res1 can be assigned to (Arg2) -> Res2 if Res1: Res2 and Arg2: Arg1.

To express this, let's tweak a little bit the first closure:

import Foundation

let nsErrorHandler: (CustomStringConvertible) -> NSError = { _ in
return NSError(domain: "", code: 0, userInfo: nil)
}
var anyHandler: (Int) -> Error = nsErrorHandler

The above code works because Int conforms to CustomStringConvertible, while NSError conforms to Error. Any would've also work instead of Error as it's even more generic.

Now that we established that, let's see what happens in your two blocks of code.

The first block tries to assign a more specific argument closure to a less specific one, and this doesn't follow the variance rules, thus it doesn't compile.

How about the second block of code? We are in a similar scenario as in the first block: closures with one argument.

  • we know that String, or Void, is more specific that Any, so we can use it as return value
  • (Int) -> Void is more specific than (Any) -> Void (closure variance rules), so we can use it as argument

The closure variance is respected, thus intResolver and stringResolver are a compatible match for anyResolver. This sounds a little bit counter-intuitive, but still the compile rules are followed, and this allows the assignment.

Things complicate however if we want to use closures as generic arguments, the variance rules no longer apply, and this due to the fact that Swift generics (with few exceptions) are invariant in respect to their type: MyGenericType<B> can't be assigned to MyGenericType<A> even if B: A. The exceptions are standard library structs, like Optional and Array.

Swift generic closures sum

You are running into the issue of Swift generic coercion misunderstanding. Generic types in Swift are invariant, which means that Styler<A> and Styler<B> are completely unrelated types even if A and B are related (subclasses for instance).

This is why Style<UILabel> and Styler<UIView> are unrelated. However, closures (and hence functions) are variant (as explained here) - covariant on the return type, and contravariant on the parameter types, this is why your first example works.

Because of this, you can pass a UILabel to a Styler<UIView>.apply, since that is a simple function call, which accepts subclasses of the declared input argument type.

let styler1: Styler<UIView> = Styler { (label: UIView) -> Void in
label.backgroundColor = UIColor.red
}

let styler2: Styler<UIView> = Styler { (view: UIView) -> Void in
view.backgroundColor = UIColor.green
}

let styler3 = styler1 + styler2

styler1.apply(UILabel())

Swift optional promotion vs generic overload resolution

Specificity seems to always trump variance conversions, according to my experiments. For example:

func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }

bar([1], Y()) // A

bar is more specific, but requires a variance conversion from [Int] to [Any].

For why you can convert from (Y?) -> Void to (P) -> Void, see this. Note that Y is a subtype of Y?, by compiler magic.

Since it is so consistent, this behaviour seems to be by design. Since you can't really make Y not a subtype of Y?, you don't have a lot of choices if you want to get the desired behaviour.

I have this work around, and I admit it's really ugly - make your own Optional type. Let's call it Maybe<T>:

enum Maybe<T> {
case some(T)
case none

// implement all the Optional methods if you want
}

Now, your (Maybe<Y>) -> Void won't be converted to (P) -> Void. Normally I wouldn't recommend this, but since you said:

in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.

I thought reinventing Optional might be worth it.

Swift: What's wrong with this math function?

The size of Int is 32-bit on 32-bit platforms (e.g. iPhone 5 and below), but the timestamp nowadays is in the range 0x57300000, which will certainly overflow when you multiply it by 2. In Swift arithmetic overflow will cause the program to crash.

You could force the output to be an Int64, or just return a TimeInterval (a Double) if you don't require the output to be an integer.

// swift 3
func Unixtime() -> TimeInterval {
return Date().timeIntervalSince1970
}

// swift 2
func Unixtime() -> NSTimeInterval {
return NSDate().timeIntervalSince1970
}

print((Unixtime() + 2) * 2)

How can I get first 10-bits of UInt16 type?

How about, as 0x3FF in binary is 0000 0011 1111 1111

x = (UInt16)(x & 0x3FF)

How can I get first 10-bits of UInt16 type?

How about, as 0x3FF in binary is 0000 0011 1111 1111

x = (UInt16)(x & 0x3FF)

Castle: using an existing (not single) instance for a lower-level dependency

The solution was to use ContextualLifestyle coupled with a custom factory that kept a reference to the ContainerContext, in order to use the same one when resolving another ViewModel.

Extension that makes wrapper that dispenses self typed as self

The trick to this is to define a protocol, an extension of that protocol, and put the makeHolder method in that extension. That way, you can use Self as the generic type for the returned ControlHolder.

First define a new protocol (let's call it "HoldableControl") and require that conformers must be UIControls. It doesn't need any other requirements because we just care about adding the makeHolder function to an extension.

protocol HoldableControl: UIControl {}

Then, add an extension of HoldableControl and define makeHolder in it, returning ControlHolder<Self>. We are allowed to use Self here because it is allowed in protocol extensions, unlike in an extension to UIControl.

extension HoldableControl {
func makeHolder() -> ControlHolder<Self> {
ControlHolder(control: self)
}
}

Then, we just need to have UIControl conform to this protocol:

extension UIControl: HoldableControl {}

And make your ControlHolder generic, as you've already done:

struct ControlHolder<T: UIControl> {
let control: T

init(control: T) {
self.control = control
}

func retrieve() -> T {
control
}
}

And now it will work:

let holder = UISwitch().makeHolder() // type is ControlHolder<UISwitch>
let output = holder.retrieve() // type is UISwitch

Method chaining - why is it a good practice, or not?

I agree that this is subjective. For the most part I avoid method chaining, but recently I also found a case where it was just the right thing - I had a method which accepted something like 10 parameters, and needed more, but for the most time you only had to specify a few. With overrides this became very cumbersome very fast. Instead I opted for the chaining approach:

MyObject.Start()
.SpecifySomeParameter(asdasd)
.SpecifySomeOtherParameter(asdasd)
.Execute();

The method chaining approach was optional, but it made writing code easier (especially with IntelliSense). Mind you that this is one isolated case though, and is not a general practice in my code.

The point is - in 99% cases you can probably do just as well or even better without method chaining. But there is the 1% where this is the best approach.



Related Topics



Leave a reply



Submit