Here is what happens:
infix operator <*> { associativity left precedence 150 }
func <*> <A, B>(wrappedFunction: (A -> B)?, optional: A?) -> B? {
println(optional)
if let f = wrappedFunction {
if let value = optional {
return f(value)
}
}
return nil
}
func someFnc(obj: AnyObject!) {
println("lol, wat?")
}
let constant: AnyObject? = nil
let result1: ()? = someFnc <*> constant
var variable: AnyObject? = nil
let result2: ()? = someFnc <*> variable
The result is:
nil
Optional(nil)
lol, wat?
So, our var variable: AnyObject?
becomes a nested optional AnyObject??
once it enters the <*>
function. Its value is then Optional(nil), which is not nil, so the wrapped function is called on it. Yet another workaround could be to flatten the nested optional:
let result2: ()? = someFnc <*> variable?
But we don't need a workaround here. It's not a bug, there is a subtle error in the code. The problem is that someFnc
has wrong signature. You use AnyObject!
, which is implicitly unwrapped optional, so the signature of the function is AnyObject! -> ()
. But the <*>
operator requires (A -> B)?
, not (A! -> B)?
. Just change func someFnc(obj: AnyObject!)
to func someFnc(obj: AnyObject)
and it will work. I'm not really sure why the problem appeared only while using variable, and not with the constant.
That said, I don't think that the applicative operator should be used with variables and for side effects, as in this case. Also, avoid implicitly unwrapped optionals as much as possible. We saw how nasty they are. They allowed us to use wrong function without any warning.
EDIT:
I did few tests and there is definitely some weirdness going on:
func upperCase1(string: String?) -> String {
println("calling upperCase1...")
return string?.uppercaseString ?? ""
}
func upperCase2(string: String?) -> String? {
println("calling upperCase2...")
return string?.uppercaseString
}
func upperCase3(string: String) -> String? {
println("calling upperCase3...")
return string.uppercaseString
}
func upperCase4(string: String) -> String {
println("calling upperCase4...")
return string.uppercaseString
}
let constant: String? = nil
var variable: String? = nil
let mappedConstant1 = map(constant, upperCase1) // nil
let mappedVariable1 = map(variable, upperCase1) // Some("")
let mappedConstant2 = map(constant, upperCase2) // nil
let mappedVariable2 = map(variable, upperCase2) // Some(nil)
let mappedConstant3 = map(constant, upperCase3) // nil
let mappedVariable3 = map(variable, upperCase3) // nil
let mappedConstant4 = map(constant, upperCase4) // nil
let mappedVariable4 = map(variable, upperCase4) // nil
If we use map
method instead of the global function, everything is nil
in all the cases and none of the upperCase[N]
functions is called. There is no difference in behaviour between regular and implicitly unwrapped optionals. The fact that variables and constants behave differently is not the worst thing here. The worst thing is that the compiler allows us to call upperCase1
and upperCase2
. The only two functions that should be allowed in this case are upperCase3
and upperCase4
, and the good news is that those two functions work correctly with variables.