Swift's Range Operators

I was going to wait until I had more first-hand experience with Swift before blogging any opinions, but then I remembered this is the Internet. So here I am, late to the party, weighing in on Swift's range operators, about which I have a definite opinion.

Swift's original range operators, as defined in the first beta of Xcode 6, were:

  • x..y for a half-closed range (including x, excluding y)
  • x...y for a closed range (including both x and y)

There were lots of complaints about this choice, mostly in two categories as I recall:

  • Complaint #1: .. and ... are too easy to confuse with each other, the same way = and == are easy to confuse in C.

  • Complaint #2: .. and ... are too easy to confuse with the same operators in Ruby, which are defined with the opposite meanings.

Note that these complaints aren't about personal taste. They're about making it too easy to write buggy code.

Here is a complaint that absolutely nobody made, so please don't take it too seriously, but it did occur to me as a possible annoyance:

  • Complaint #3: Some people want ... to be a system macro that automatically gets replaced with an ellipsis character.

A few people, including me, have proposed this alternative, which I am still convinced is best:

  • x..<y for half-closed
  • x..=y for closed

Here's what I like about this option:

  • There is a parallel structure to the operators. ".." means this is a range, followed by a qualifier, either "<" or "=", that indicates whether the last element of the range is less than or equal to y.

  • Neither operator is a substring of the other. You can't make the mistake of typing one too few characters.

  • The meanings of the operators are clear from their appearance (addressing Complaint #1). You won't have to pause each time to ask yourself "Which one was the closed one again?"

  • The operators don't conflict with other languages (addressing Complaint #2). When I posted my suggestion, I didn't think either operator had a precedent in any other language. It turns out ..< is used in Groovy, but it has the same meaning there as here.

In Xcode 6 beta 3, one of the range operators was changed. As of this writing, the operators are:

  • x..<y for half-closed
  • x...y for closed

This is fine with me, although I may change my mind if I ever start programming in Ruby. By only changing one of the two operators, the Swift designers[1] leave them half-open, if you will, to Complaint #2.

I want to mention one more proposed alternative that seems nice in theory but that I don't think should be adopted:

  • [x..y) for half-closed
  • [x..y] for closed

This notation is already familiar to people with a little math, and easily learnable by anyone else. It's concise. It's unambiguous. It allows for the possibility of variations like (x..y] and (x..y).

But I think it could be open to Complaint #1. Depending on the font the programmer is using, and the resolution of their monitor, and how sleep-deprived they are, a parenthesis could maybe be mistaken for a square bracket. Or the difference might not be striking enough to register when they're eyeballing the code, looking for why the program crashed.

Furthermore, I think mismatched brackets and parentheses could cause headaches in text editors other than Xcode. When we see an expression like that, we're used to being able to double-click one of the delimiters to select the whole expression. I don't know, maybe existing text editors could easily be updated to handle that, but it seems like a potential problem to me, and I do think this kind of consideration should influence language design. It's worth looking for an option that both makes theoretical sense and plays well with the tools we use in practice.


[1] To the extent there is any such person other than Chris Lattner.

Faking "switch" with an object value

Jeff Kelley started a thread on the objc-language mailing list with this idea:

I would love to see a switch statement we could use for objects, testing equality with each case: statement. @switch would work nicely as the name, and I envision it working like this:
@switch(myString) {
    case @"hello world":
        // Do something
        break;
    case @"another one":
        break;
}
This would be equivalent to writing the code using if statements and sending -isEqual: messages, but with a much more readable control flow.

The blocks approach

One suggested approach was to use a dictionary to map each "case" value to a block that should be executed when the "case" value matches the "switch" value. Jeff Biggus posted this solution mainly as an academic exercise, but I think it's about as tidy as the blocks-based approach gets:

typedef void (^voidBlock)(void);
 
#define swoosh( test_var__, action_dictionary__... ) \
        ((voidBlock)action_dictionary__[test_var__])();
 
[...]
 
NSString *test = @"that";
swoosh( test, @{
        @"this" : ^{ NSLog( @"found this" ); },
        @"that" : ^{ NSLog( @"found that" ); },
});

Nicolas Bouilleaud posted an alternative solution using +resolveInstanceMethod: that allows you to write:

[[@"foo" switch]
 case:@"bar" :^{ success = NO; }
 case:@"baz" :^{ success = NO; }
 case:@"foo" :^{ success = YES; }
 ];

I'm not crazy about using blocks for a few reasons:

  • You have to think about whether you need to use __block variables.
  • You can't put break, continue, or return statements in the blocks and have them work as they would in an analogous switch statement; often their very presence would be a syntax error.
  • If you use a dictionary of blocks, the "case" objects have to conform to NSCopying so that they can be dictionary keys. Also, as Nicolas points out, if you use a dictionary, you can't specify the order in which the cases are tested. So, no dictionaries; but you can imagine a similar approach using an array.
  • Xcode's auto-indenting of blocks looks really, really horrible. In theory, the typographical quirks of an IDE have nothing to do with the soundness of a technical approach, but Xcode is so bad about this that I can't ignore it. For example, if I put the first NSLog above on its own line, Xcode does this:
swoosh( test, @{
       @"this" : ^{
    NSLog( @"found this" );
},
       @"that" : ^{ NSLog( @"found that" ); },
       });

objswitch, objcase, endswitch

I don't know how original this is, but by using a few macros and a small class used behind the scenes, I came up with a different approach that is essentially syntactic sugar around nested else-if statements. Here's an example of how it looks, as formatted by Xcode:

objswitch(someObject)
objcase(@"one")
{
    // Nesting works.
    objswitch(@"b")
    objcase(@"a") printf("one/a");
    objcase(@"b") printf("one/b");
    endswitch
 
    // Any code can go here, including break/continue/return.
    // Xcode will indent it nicely.
}
objcase(@"two") printf("It's TWO.");  // Can omit braces.
objcase(@"three",  // Can have multiple values in one case.
        @"tres",
        @"trois") { printf("It's a THREE."); }
defaultcase printf("None of the above.");  // Default case is optional.
endswitch

I would argue this is even a tiny bit nicer than switch/case syntax, because you don't need unsightly break statements to keep the cases from bleeding into each other.

If someObject is @"one", the output is

oneb

If someObject is @"tres", the output is

It's a THREE.

In this example all the "case" values are strings, but they can be any object.

The "keywords" objswitch, objcase, objkind, and endswitch are actually macros. There is a simple class called ObjectMatcher that is instantiated by objswitch. You can see the code on GitHub.

objkind

Some people mentioned wanting similar syntax for testing the class of the object rather than its value. Testing an object's class is often a sign of suboptimal design (see here for a brief discussion, here for my thoughts on the matter), but for those occasions when you decide it's the right approach, you can use my objkind macro:

objswitch(someObject)
objkind(NSNumber) { printf("It's a NUMBER."); }
objkind(NSString) { printf("It's a STRING."); }
objkind([NSArray class],
        [NSDictionary class],
        [NSSet class]) printf("It's a collection.");
endswitch

Note that if you're only passing one class name to objkind, you can just give the class name:

objkind(NSNumber)

But if you pass multiple classes, you have to say [MyClass class] or [MyClass self] (or, if you prefer, MyClass.class or MyClass.self) for every item after the first one. This is a limitation of __VA_ARGS__ macros. I prefer all the items to look the same — hence:

objkind([NSArray class],
        [NSDictionary class],
        [NSSet class])

Like objcase, objkind is just a wrapper around a nested else-if statement, so you can freely mix objcase and objkind within the same objswitch.

selswitch, selcase

One place I might like a similar construct is for doing a switch statement on a selector. I have validateUserInterfaceItem: methods all over the place with lots of nested if-statements like this:

SEL itemAction = [anItem action];
 
if (itemAction == @selector(selectSuperclass:))
{
    // ...
}
else if (itemAction == @selector(selectAncestorClass:))
{
    // ...
}
else if ((itemAction == @selector(selectFormalProtocolsTopic:))
         || (itemAction == @selector(selectInformalProtocolsTopic:))
         || (itemAction == @selector(selectFunctionsTopic:)))
{
    // ...
}

I created selswitch and selcase macros that let you do this:

selswitch([anItem action])
selcase(@selector(selectSuperclass:))
{
    // ...
}
selcase(@selector(selectAncestorClass:))
{
    // ...
}
selcase(@selector(selectFormalProtocolsTopic:),
        @selector(selectInformalProtocolsTopic:),
        @selector(selectFunctionsTopic:))
{
    // ...
}
endswitch

I haven't decided whether the improvement is big enough that I'd use this rather than plain old nested ifs, especially since validateUserInterfaceItem: is called very frequently and should be as fast as possible.

[UPDATE: I've made several edits since I first published this post, ranging from fixing typos to inserting a sentence or two. I was originally flagging each change, but to reduce clutter I decided just to add this mention at the bottom.]

Writing a .service bundle

You can provide system services (items that appear in the Services menu) in two ways: through an application (YourApp.app) or through a standalone service (YourService.service). The first way is well documented, but I've found it hard to find instructions for the second way.

The first way: a regular .app

Your Cocoa application can provide services that appear in the Services menu. Apple's "Services Implementation Guide" explains how to do this. Basically:

  • Create a class (or use an existing class) with one method for each service. Each service receives input and optionally returns output by way of NSPasteboard.
  • Add an "NSServices" key to Info.plist with info about each service.
  • At some point during application startup, call NSRegisterServicesProvider().

Put the application in /Applications, or a subfolder thereof, to get the system to detect the new service(s).

When an application service is invoked, the application becomes active. There seems to be no way to avoid this. At best you can immediately hide the application, but there will still be a flicker as windows appear and disappear.

UPDATE: Mark Munz offered this tip on the cocoa-dev mailing list:

[T]he solution I used to prevent Services from bringing the app forward is to have a background (LSUIElement) helper app that acts as the NSServices provider. Depending on what you need to do, you could either support the service directly in the helper app or use it to talk to the parent app to perform the necessary work (without requiring it to activate). I use the second approach.

The second way: a .service bundle

If you want a service that is purely a background operation, you can create a standalone service that has no UI. It's very simple once you know how, but I haven't found a lot of help online, although some books discuss it and you can find examples on GitHub by searching for NSRegisterServicesProvider.

I expected a "System Service" template in Xcode, but there is none. Instead, you use a "Cocoa Application" project and code it just like a regular application with services except:

  • Change the target's wrapper extension from "app" to "service": Project > Target > Build Settings > Wrapper Extension.
  • Make it a background application by adding to Info.plist: LSUIElement = YES. (This is the "Application is background only" item in the popup menu that appears in Xcode's plist editor.)
  • Remove resources you won't need, like MainMenu.xib, AppDelegate, and Credits.rtf.
  • Edit main.m to instantiate the service-provider object and enter a run loop. Here's an example.

Build the project and copy YourService.service to ~/Library/Services. You may have to relaunch apps to get them to see the service.

Tips

  • Instead of editing Info.plist to add the NSServices entries, you might prefer to use Xcode's UI. I don't know when this was added to Xcode, but you can go to Project > Target > Info and there's a "Services" area at the bottom. It's easy to miss if you don't know it's there.

  • To uninstall the service, remove YourService.service from ~/Library/Services. You won't be able to delete it if the service has a running process, so you might have to kill the process first.

  • If the service doesn't appear in the Services menu, you may have to force the menu to be reconstructed. Unfortunately there's no command you can call from the command line. It's trival to write one though — all it has to do is call NSUpdateDynamicServices().

See also

As I mentioned, Apple's "Services Implementation Guide" has plenty of detail on writing application services, but as far as I can tell the only help it gives on standalone services is this one line:

> To build a standalone service, use the extension .service and store it in Library/Services.

The breakthrough that got me going was when I found example code on the Timac blog. I already had implemented a service in an application. By looking at Timac's code and doing monkey-see-monkey-do, I got my first standalone service working.

I then realized I could find plenty more examples by searching GitHub for "NSRegisterServicesProvider".

Here's the service I wrote. It copies method names to the clipboard so you can paste them in emails, code comments, docs, and so forth.