Consider the following four functions. Off the top of your head, without cheating, can you tell me which ones will compile and which ones won’t? And can you tell me what the type of newDate
will be for each function? (In these examples, dateFormatter
is a DateFormatter
, oneDayOffset
is DateComponents
, and calendar
is Calendar
.)
func tomorrowsDateString1(dateString: String) -> String? { let date = dateFormatter.date(from: dateString) let newDate = date.map{ calendar.date(byAdding: oneDayOffset, to: $0) } return newDate.map(dateFormatter.string(from:)) } func tomorrowsDateString2(dateString: String) -> String? { let date = dateFormatter.date(from: dateString) let newDate = date.flatMap{ calendar.date(byAdding: oneDayOffset, to: $0) } return newDate.flatMap(dateFormatter.string(from:)) } func tomorrowsDateString3(dateString: String) -> String? { let date = dateFormatter.date(from: dateString) let newDate = date.flatMap{ calendar.date(byAdding: oneDayOffset, to: $0) } return newDate.map(dateFormatter.string(from:)) } func tomorrowsDateString4(dateString: String) -> String? { let date = dateFormatter.date(from: dateString) let newDate = date.map{ calendar.date(byAdding: oneDayOffset, to: $0) } return newDate.flatMap(dateFormatter.string(from:)) }
And now consider this cute little hack, which will probably ensure you end up in hell:
/// High precedence precedencegroup HighPrecedence { higherThan: BitwiseShiftPrecedence } /// .? contextually performs map or flatMap infix operator .?: HighPrecedence /// Performs an in-line operator-based flatMap public func .?<T, U>(lhs: T?, transform: (T) throws -> U?) rethrows -> U? { return try lhs.flatMap(transform) } /// Performs an in-line operator-based map public func .?<T, U>(lhs: T?, _ transform: (T) throws -> U) rethrows -> U? { return try lhs.map(transform) }
Which gives you this little one-operator hackapalooza:
/// And no, you don't have to figure out if you need map or flatMap func tomorrowsDateString(dateString: String) -> String? { let date = dateFormatter.date(from: dateString) let newDate = date.?{ calendar.date(byAdding: oneDayOffset, to: $0) } return newDate.?dateFormatter.string(from:) }
Pray for my soul.
2 Comments
Wow, it is so hacky and confusing my head hurts!
I’m gonna lead a lot of help to digest this post 😛
I feel like I need a step by step walk through.
Anyway, interesting post (I think for what I understood) and it was really funny to play “which one compiles?” 🙂
Not sure I understand why tomorrowsDateString3 doesn’t end up double optional. Curious.