This question is a follow up to my earlier question: I expected the system to report non protocol conformance, but it does not! Why?
Please read the referred question for you to get a better idea of the constraints at hand.
I created a generic function in Swift that will reject its parameter unless such a parameter is Optional
. The function I created fully works and does what I desire.
Meaning, any calls to onlyCallableByAnOptable(...)
, even inside an if let
, will yield error due to non-protocol conformance, exactly as desired.
Errors like: Argument type 'UIColor' does not conform to expected type 'Optable'
My only question is: Is there a simpler solution?
To make it clear:
func onlyCallableWithAnOptinalParameter<T>(:T)->T
needs to work when called in anif let
statement likefunc test()
does.
protocol Optable {
associatedtype OptableType
func optionalOptable() -> OptableType?
}
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 optionalOptable() -> OptableType? {
return self
}
}
class TestOptable {
static func test()
{
let c = UIColor.blue
let s = "hi"
let i = Int(10)
let oi: Int? = 10
if let v = onlyCallableByAnOptable(c) { // ERROR, as was desired.
print("color \(v)")
}
if let v = onlyCallableByAnOptable(s) { // ERROR, as was desired.
print("string \(v)")
}
if let v = onlyCallableByAnOptable(i) { // ERROR, as was desired.
print("integer \(v)")
}
if let v = onlyCallableByAnOptable(oi) { // OK, as expected.
print("optional integer \(v)")
}
}
}
You might want to give this protocol a better name, but I don't foresee any problems with it as-is, unless you're making your own ExpressibleByNilLiteral
types that don't wrap.
protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
associatedtype Wrapped
}
extension Optional: ExpressibleByNilLiteral { }
func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
optional as? Optional.Wrapped
}
Recommendation: use an initializer. (Downside is the argument label being necessary to disambiguate, but I personally like the explicitness because of how weird this case is. i.e. Swift makes it easy to enforce that something is not optional, but not vice versa.)
extension Optional: ExpressibleByNilLiteral {
init<Optional: ExpressibleByNilLiteral>(optional: Optional) where Optional.Wrapped == Wrapped {
self = optional as? Wrapped
}
}
+
if let v = Optional(optional: i) { // ERROR, as was desired.
print("integer \(v)")
}
if let v = Optional(optional: oi) { // OK, as expected.
print("optional integer \(v)")
}