[swift-users] Making functions generic on optionals

Kenny Leung kenny_leung at pobox.com
Fri Feb 5 17:54:35 CST 2016


Thanks for your answers.

After some googling and experimenting, I get it now:

    func testOptionalMap() {
        do {
            var sortString :String?
            var sortDescriptors :[NSSortDescriptor]?

            sortString = "name,up"
            try sortDescriptors = sortString.map {try NSSortDescriptor.sortDescriptorsFromString($0)}
            XCTAssertNotNil(sortDescriptors);
            if let sortDescriptor = sortDescriptors?[0] {
                assert(sortDescriptor, "name",   true)
            }

            sortString = nil
            try sortDescriptors = sortString.map {try NSSortDescriptor.sortDescriptorsFromString($0)}
            XCTAssertNil(sortDescriptors);
        } catch {
            XCTFail()
        }
    }

I commented out the stub method, yet I can still send an optional into sortDescriptorsFromString.

I have to say, though, this is a very unfriendly way to solve this problem.

- since optionals are so hidden syntax wise, you could naturally assume that map belongs to String, and not Optional
- reading the code, it’s not obvious what the map function is actually doing here. You really have to squint your eyes to realize that 

“Optional.map will only execute the code in the block if the thing it’s wrapping is not nil, and $0 is the thing it’s wrapping, unwrapped"

So I’ll try rephrasing my question:

Is there way to declare a method such that, if the argument is optional, the return value is optional, but if the argument is not optional, the return value is also not optional? The argument and the return values are not the same type.

Thanks!

-Kenny


> On Feb 5, 2016, at 14:45, Kenny Leung via swift-users <swift-users at swift.org> wrote:
> 
> Hi Jordan.
> 
> Thanks for the response.
> 
>> On Feb 5, 2016, at 2:34 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>> 
>> The smallest way to write this is at the call site:
>> 
>> sortStringOpt.map { NSSortDescriptor.sortDescriptorsFromString($0) }
> 
> Sorry, but I don’t understand this solution. What type is sortStringOpt?
> 
>> But really I'm a little curious about why you're thinking about "every time [you] want to do this". Does this really come up that often? In this particular case, why would you ever not have a sort string?
> 
> In particular, sortString might come from user input, so it may be nil.
> 
> In general,  “every time I want to do this” was referring to providing a piece of API that could take optional or non-optional inputs without either burdening the provider with writing a stub method or the client with having to always deal with an optional return from the method.
> 
> -Kenny
> 
> 
>> Jordan
>> 
>> 
>>> On Feb 5, 2016, at 13:53, Kenny Leung via swift-users <swift-users at swift.org> wrote:
>>> 
>>> Hi All.
>>> 
>>> In the code below, I have the method
>>> 
>>> public class func sortDescriptorsFromString(sortString :String) throws -> [NSSortDescriptor]
>>> 
>>> In order to make life easier, I would like to make sortString optional, but then I would have to make the return type optional to to be able to return nil if the argument is nil. To get around this, I’ve added a stub method
>>> 
>>> public class func sortDescriptorsFromString(sortString :String?) throws -> [NSSortDescriptor]?
>>> 
>>> that declares the argument to be optional and the return type to be optional.
>>> 
>>> I get the feeling that there is a way to not have to write a stub method every time I want to do this. I also get the feeling that I should be able to accomplish it through generics. But I do not know how to write the declaration for such a method. Can anyone help?
>>> 
>>> Thanks!
>>> 
>>> -Kenny
>>> 
>>> 
>>> extension NSSortDescriptor {
>>> 
>>>    public class func sortDescriptorsFromString(sortString :String) throws -> [NSSortDescriptor] {
>>>        var descriptors = [NSSortDescriptor]()
>>>        let components = sortString.split("[, ]+")
>>> 
>>>        for i in 0.stride(to: components.count, by: 2) {
>>>            let key = components[i]
>>>            let direction = components[i + 1]
>>>            var descriptor :NSSortDescriptor?
>>> 
>>>            if SORT_STRINGS_ASCENDING.contains(direction) {
>>>                descriptor = NSSortDescriptor(key: key, ascending: true)
>>>            } else if SORT_STRINGS_DESCENDING.contains(direction) {
>>>                descriptor = NSSortDescriptor(key: key, ascending: false)
>>>            } else if SORT_STRINGS_CASEINSENSITIVE_ASCENDING.contains(direction) {
>>>                descriptor = NSSortDescriptor(key: key, ascending: true, selector: "caseInsensitiveCompare:");
>>>            } else if SORT_STRINGS_CASEINSENSITIVE_DESCENDING.contains(direction) {
>>>                descriptor = NSSortDescriptor(key: key, ascending: false, selector: "caseInsensitiveCompare:");
>>>            } else if SORT_DATE_ASCENDING.contains(direction) {
>>>                descriptor = NSSortDescriptor(key: key, ascending: true, selector: "compare:");
>>>            } else if SORT_DATE_DESCENDING.contains(direction) {
>>>                descriptor = NSSortDescriptor(key: key, ascending: false, selector: "compare:");
>>>            }
>>> 
>>>            if let nnDescriptor = descriptor {
>>>                descriptors.append(nnDescriptor)
>>>            } else {
>>>                throw NSSortDescriptorError.UnsupportedSortDirection
>>>            }
>>>        }
>>> 
>>>        return descriptors
>>>    }
>>> 
>>>    public class func sortDescriptorsFromString(sortString :String?) throws -> [NSSortDescriptor]? {
>>>        guard let sortString = sortString else {return nil}
>>>        return try self.sortDescriptorsFromString(sortString)
>>>    }
>>> 
>>> }
>>> 
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-users
>> 
> 
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org <mailto:swift-users at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160205/d40319c0/attachment.html>


More information about the swift-users mailing list