Should a transformation function take a nullable and return a nullable or should the caller handle nullability

by Michael Ozeryansky   Last Updated October 19, 2019 08:05 AM

Often I need to transform a type to another, such as a networking model to a data model, or a data model to a binary representation. Should these transformation functions take an Optional/nullable value and immediately return nil if it's nil, or should the function only accept non-nil values and always return a non-nil transformation? Assume my code properly handles validation when receiving data, so no further validation is needed, just transformation.

Example 1: Have the caller handle nil objects

func makeUser(from apiUser: APIUser) -> User {
    return User(id: apiUser.id)
}

var user: User? = nil
func downloadUser() {
    service.getUser { (apiUser: APIUser?) in
        if let apiUser = apiUser {
            self.user = makeUser(from: apiUser)
        } else {
            self.user = nil
        }
    }
}

Example 2: Have the transform handle nil objects

func makeUser(from apiUser: APIUser?) -> User? {
    if apiUser == nil {
        return nil
    }
    return User(id: apiUser.id)
}

var user: User? = nil
func downloadUser() {
    service.getUser { (apiUser: APIUser?) in
        self.user = makeUser(from: apiUser)
    }
}

Example 1 explicitly states an APIUser will always transform to a User object. While Example 2 implicitly implies that a valid APIUser may not map to a valid User object, even though that is guaranteed.

While Example 2 does demonstrate that the common logic is handled in the transform instead of copied each time it's needed, I personally don't like the fact that the transform can return an optional/nullable value even though my function contract states validation has already been performed and a valid value will never fail.



Related Questions


Updated May 31, 2016 08:02 AM

Updated April 30, 2017 06:05 AM

Updated October 02, 2019 19:05 PM

Updated November 08, 2017 04:05 AM

Updated June 26, 2016 08:02 AM