Skip to content

Instantly share code, notes, and snippets.

@Westacular
Created December 11, 2017 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Westacular/621612b07c425dcd4d2e95c6346a85db to your computer and use it in GitHub Desktop.
Save Westacular/621612b07c425dcd4d2e95c6346a85db to your computer and use it in GitHub Desktop.
Testing the covariance/contravariance behaviours of casting function types, both directly and in a generic context. There are several inconsistencies.
protocol Foo { }
protocol Bar { }
protocol Baz { }
struct FooStruct: Foo, Bar {}
/// Test covariance of return type
let block: () -> FooStruct = { FooStruct() }
block is () -> FooStruct // true
block is () -> Foo // false, but expected true
block is () -> Bar // false, but expected true
block is () -> Baz // false
block as? () -> FooStruct // non-nil
block as? () -> Foo // non-nil
block as? () -> Bar // non-nil
block as? () -> Baz // nil
func testIs<T,U>(_ returnType: U.Type, _ block: @escaping ()->T) -> Bool {
return block is () -> U
}
testIs(FooStruct.self, block) // true
testIs(Foo.self, block) // false, but expected true
testIs(Bar.self, block) // false, but expected true
testIs(Baz.self, block) // false
func testAs<T,U>(_ returnType: U.Type, _ block: @escaping ()->T) -> ( () -> U )? {
return block as? () -> U
}
testAs(FooStruct.self, block) // non-nil
testAs(Foo.self, block) // nil, but expected non-nil
testAs(Bar.self, block) // nil, but expected non-nil
testAs(Baz.self, block) // nil
func testRun<T,U>(_ returnType: U.Type, _ block: @escaping ()->T) -> Bool {
return block() is U
}
testRun(FooStruct.self, block) // true
testRun(Foo.self, block) // true
testRun(Bar.self, block) // true
testRun(Baz.self, block) // false
/// Test contravariance of parameter types
let block2: (Foo) -> () = { _ in return }
block2 is (Foo) -> () // true
block2 is (FooStruct) -> () // false, but expected true
block2 as? (Foo) -> () // non-nil
block2 as? (FooStruct) -> () // non-nil
func testIs2<T,U>(_ paramType: U.Type, _ block: @escaping (T)->()) -> Bool {
return block is (U) -> ()
}
testIs2(Foo.self, block2) // true
testIs2(FooStruct.self, block2) // false, but expected true
func testAs2<T,U>(_ paramType: U.Type, _ block: @escaping (T)->()) -> ( (U) -> () )? {
return block as? (U) -> ()
}
testAs2(Foo.self, block2) // non-nil
testAs2(FooStruct.self, block2) // nil, but expected non-nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment