2

I am using Xcode Version 11.3.1 (11C504)

I am trying to create a generic function in Swift that will reject its parameter unless such a parameter is Optional.

In the following code, I was expecting the system to report errors in all calls to onlyCallableByAnOptable() made inside test(), because none of them provide an optional value as a parameter.

However, the system only reports non-protocol conformance if I remove the Optional extension that conforms to Optable!

Which to me, it means that the system is regarding any and all values as Optional, regardless!

Am I doing something wrong?

(By the way, the following code used to be working as expected in earlier versions of Swift. I just recently found out that it stopped working, for it was letting a non-Optional go through.)

protocol Optable {
    func opt()
}

func onlyCallableByAnOptable<T>( _ value: T) -> T where T: Optable {
    return value
}

// Comment the following line to get the errors 
extension Optional: Optable { func opt() {} }


class TestOptable {
    static func test() 
    {
        let c = UIColor.blue
        let s = "hi"
        let i = Int(1)

        if let o = onlyCallableByAnOptable(c) { print("color \(o)") }
        //^ expected ERROR: Argument type 'UIColor' does not conform to expected type 'Optable'

        if let o = onlyCallableByAnOptable(s) { print("string \(o)") }
        //^ expected ERROR: Argument type 'String' does not conform to expected type 'Optable'

        if let o = onlyCallableByAnOptable(i) { print("integer \(o)") }
        //^ expected ERROR: Argument type 'Int' does not conform to expected type 'Optable'
    }
}
SirEnder
  • 564
  • 4
  • 14

2 Answers2

1

Since you've made all Optionals conform to Optable and you are using the if let syntax to unwrap the result of the call to onlyCallableByAnOptable (which means the return type must be some kind of Optional, which means the parameter must also be that same type of Optional because both the parameter and the return type are of type T in your generic method), Swift is inferring the types being passed in as UIColor?, String?, and Int? (implicitly wrapping them in Optionals) instead of UIColor, String and Int.

TylerP
  • 9,600
  • 4
  • 39
  • 43
  • 1
    Mmm!... So it has to do with function return type inference! – SirEnder Mar 05 '20 at 07:46
  • I wanted swift to infer the type solely based on the parameter provided not also based on the expected return type (i.e context). Is there a simple way to achieve this? – SirEnder Mar 05 '20 at 07:50
  • You could try changing your function's return type to `T?`, if you always want it to return an optional type, like: `func onlyCallableByAnOptable(_ value: T) -> T? where T: Optable`. Not sure if that's exactly what you're looking for, though. Doing that, I then get the errors that you would expect (e.g. "global function 'onlyCallableByAnOptable' requires that 'UIColor' conform to 'Optable'", etc.) – TylerP Mar 05 '20 at 07:54
  • 1
    Ok. No worries. If I happen to find a way to do what I wanted, I will then post it here as an added answer. – SirEnder Mar 05 '20 at 08:04
  • I added an answer. What do you think? Am I overcomplicating? – SirEnder Mar 05 '20 at 08:43
0

I am the one who posted this question.

I was trying to create a generic function in Swift that would reject its parameter unless such parameter is an Optional.

As @TylerTheCompiler pointed out, using my original implementation (in the question), Swift was inferring type T (used in onlyCallableByAnOptable()), based on the full context of the call, not solely on the type of the value provided as parameter to it, therefore inferring T to be an Optional.

For the sake of helping others who might be trying to achieve the same as I was, the following is my solution to the problem I had.

All calls to onlyCallableByAnOptable(...) now correctly yield errors due to non-protocol conformance.

Errors like: Argument type 'UIColor' does not conform to expected type 'Optable'

If anyone knows of a simpler solution, please do post it as an answer to: How to create a generic function in Swift that will reject the given parameter unless it is an Optional?.

protocol Optable {
    associatedtype OptableType
    func optionalOptable() -> OptableType?
    func opt()
}

func onlyCallableByAnOptable<T>( _ value: T) -> T.OptableType? where T: Optable {
    return value.optionalOptable()
}


extension Optional: Optable {
    typealias OptableType = Wrapped //: Wrapped is the type of the element, as defined in Optional
    func opt() {}
    func optionalOptable() -> OptableType? {
        return self
    }
}


class TestOptable {
    static func test()
    {
        let c = UIColor.blue
        let s = "hi"
        let i = Int(1)

        if let o = onlyCallableByAnOptable(c) {  // ERROR, as was desired.
            print("color \(o)") 
        }
        if let o = onlyCallableByAnOptable(s) {  // ERROR, as was desired.
            print("string \(o)") 
        }
        if let o = onlyCallableByAnOptable(i) {  // ERROR, as was desired.
            print("integer \(o)") 
        }
    }
}
SirEnder
  • 564
  • 4
  • 14