An ObjC 3.0 What-If

Many had expected Swift to be more an Objective-C 3.0 than it turned out to be. But what could we have expected such a hypothetical language to look like?

Before Swift

Possibly unknown to many developers is that once upon a time, when NeXT was brought into to create what eventually would be OS X, there was an attempt to give ObjC a “Modern” syntax (read: more Java-like).

It had

object = [[MyClass alloc] init];
[object doSomething:foo withBar:bar];

Turning into:

object = (MyClass.alloc).init;
object.doSomething(foo, bar);

I should admit I’m a bit unsure of the exact syntax, the ObjC-Java bridge would have you use object.doSomethingWithBar(foo, bar), so that might be more accurate.

The idea was to attract developers that might be put off by the unfamiliar Objective-C syntax (does this sound familiar?).

Swift goes for a similar dot.functionName(… params …) syntax, but luckily recognises the need for parameter names. I think it’s probably the best compromise this side of the divide. (The other way would have been to drop the brackets and move the syntax closer to Smalltalk. Many of the alternatives mentioned in my previous post did exactly that.)

Transforming ObjC in a Swift manner

What interests me would be what Swift could have looked like if it had been an Objective-C 3.0 instead of a whole new language.

I’m going to start with a sample in Objective-C, transform it piece-by-piece and see what it would look like.

Here’s the code we’re going to start with.

@interface Foo
- (instancetype)initWithBar:(Bar *)bar;
- (void)iron:(NSString *)type using:(Tool *)tool;
@property (nonatomic, weak) id delegate;
@end

// ---

@interface Foo ()
@property (nonatomic, strong) NSString *name;
@end

@implementation Foo
{
  Bar *_bar;
}

- (instancetype)initWithBar:(Bar *)bar {
  if ((self = [super init])) {
    _bar = bar;
  }
  return self;
}

- (void)iron:(NSString *)type using:(Tool *)tool {
  if ([_bar wield:type using:tool]) {
    NSLog(@"Horray %@", self.name);
  }
}
@end

We’re ready to start.

Swift-style method calls

We start by transforming the Objective-C message passing syntax of [foo doSomething:bar] into foo.doSomething(bar) just like Swift does, but we’re holding off putting the variable declaration last:

@interface Foo
- (instancetype)initWithBar(Bar *bar);
- (void)iron(NSString *type, using: Tool *tool);
@property (nonatomic, weak) id delegate;
@end

// ---

@interface Foo ()
@property (nonatomic, strong) NSString *name;
@end

@implementation Foo
{
  Bar *_bar;
}

- (instancetype)initWithBar(Bar *bar) {
  if ((self = super.init())) {
    _bar = bar;
  }
  return self;
}

- (void)iron(NSString *type, using:Tool *tool) {
  if (_bar.wield(type, using:tool)) {
    NSLog(@"Horray %@", self.name);
  }
}
@end

Defining variables

Next up, let’s allow defining variables in the implementation directly, we’re going to borrow the syntax a from Eero. This is how Eero does it:

String name   {readonly}

And our code afterwards:

@interface Foo
- (instancetype)initWithBar(Bar *bar);
- (void)iron(NSString *type, using: Tool *tool);
id delegate {weak};
@end

// ---

@implementation Foo

Bar *_bar;
NSString *name; 

- (instancetype)initWithBar(Bar *bar) {
  if ((self = super.init())) {
    _bar = bar;
  }
  return self;
}

- (void)iron(NSString *type, using:Tool *tool) {
  if (_bar.wield(type, using:tool)) {
    NSLog(@"Horray %@", self.name);
  }
}
@end

We’ve skimmed over one detail though. For the ObjC original we get to choose if we generate accessors or not. In our example, _bar was an ivar without any accessors at all.

At this point we need to decide what’s a reasonable default:

  1. Create getter/setter by default.
  2. Create getter/setter only by annotation.
// Alternative 1
Bar *_bar {noaccessor};
NSString *name; 

// Alternative 2
Bar *_bar;
NSString *name {readwrite};

I’m going to assume that the former is the best alternative, although it could be argued that the opposite is more reasonable. Note that non-atomic rather than atomic is considered the default (unlike normal ObjC).

Eliminating the *

Since classes are never statically allocated, we can actually decide to drop the pointer symbol. Our code now looks like this:

@interface Foo
- (instancetype)initWithBar(Bar bar);
- (void)iron(NSString type, using: Tool tool);
id delegate {weak};
@end

// ---

@implementation Foo
Bar _bar {noaccessor};
NSString name; 

- (instancetype)initWithBar(Bar bar) {
  if ((self = super.init())) {
    _bar = bar;
  }
  return self;
}

- (void)iron(NSString type, using:Tool tool) {
  if (_bar.wield(type, using:tool)) {
    NSLog(@"Horray %@", self.name);
  }
}
@end

 Default values

Nothing prevents us from adding default values as syntactic sugar. If we imagine the function doSomething:(Foo *)foo using:(Bar *)bar, we could imagine the compiler generating a doSomething:(Foo *)foo which would call the former method with a default value for the second parameter. This would correspond to Swift’s default parameters:

@interface Foo
- (instancetype)initWithBar(Bar bar);
- (void)iron(NSString type, [using: Tool tool]);
id delegate {weak};
@end

// ---

@implementation Foo
Bar _bar {noaccessor};
NSString name; 
- (instancetype)initWithBar(Bar bar) {
  if ((self = super.init())) {
    _bar = bar;
  }
  return self;
}

- (void)iron(NSString type, using:Tool tool = Shovel.new() ) {
  if (_bar.wield(type, using:tool)) {
    NSLog(@"Horray %@", self.name);
  }
}
@end

 Swapping @ for braces

Swift uses braces to enclose the class definition, we can do the same and drop the @ prefix. At the same time we can let “” default to NSString type strings an c”” to represent c style strings:

interface Foo
{
  - (instancetype)initWithBar(Bar bar);
  - (void)iron(NSString type, [using: Tool tool]);
  id delegate {weak};  
}

// ---

implementation Foo
{
  Bar _bar {noaccessor};
  NSString name; 

  - (instancetype)initWithBar(Bar bar) {
    if ((self = super.init())) {
      _bar = bar;
    }
    return self;
  }

  - (void)iron(NSString type, using:Tool tool = Shovel.new() ) {
    if (_bar.wield(type, using:tool)) {
      NSLog("Horray %@", self.name);
    }
  }  
}

Some more tweaks

if ((self = [super init])) is such a common pattern that we could make special syntax and behaviour for init methods. Let’s make init methods special by a) removing -(instancetype), b) implicitly transforming super.initXXXX() into if (!(self = super.initXXXX())) return nil; and returning self by default.

In other words this:

- (instancetype)initWithBar(Bar bar) {
  if ((self = super.init())) {
    _bar = bar;
  }
  return self;
}

Becomes:

initWithBar(Bar bar) {
  super.init();
  _bar = bar;
}

Furthermore, let us replace -(MyClass) with MyClass, and +(MyClass) with class MyClass. This is the result:

interface Foo
{
  initWithBar(Bar bar);
  void iron(NSString type, [using: Tool tool]);
  id delegate {weak};  
}

// ---

implementation Foo
{
  Bar _bar {noaccessor};
  NSString name; 

  initWithBar(Bar bar) {
    super.init();
    _bar = bar;
    return self;
  }

  void iron(NSString type, using:Tool tool = Shovel.new() ) {
    if (_bar.wield(type, using:tool)) {
      NSLog("Horray %@", self.name);
    }
  }  
}

We can compare this with the corresponding Swift:

class Foo
{
  private let bar: Bar
  weak var delegate: AnyObject?
  private name : NSString?
  
  public init(bar: Bar)
  {
    self.bar = bar
    super.init() 
  }

  public iron(type: NSString, using tool: Tool = new Shovel()) {
    if _bar.wield(type, using:tool) {
      NSLog("Horray %@", self.name ?? "")
    }
  }  
}

Our final version retains the header, which we obviously could fold into our code as well, and we haven’t adopted Swift’s type-after-variable declarations, but otherwise they are fairly similar.

What we’ve done here is simply adopting the Swift syntax for Objective-C. Nothing forces us to stop there. All the languages mentioned in my previous blog entry go further, adding reasonable extensions that easily could fit into an Objective-C 3.0.

Lots of the initial buzz around Swift was how it was going to make development simpler than ObjC. With some syntactic changes, we could have an ObjNext with a syntax like Swift, but a runtime model like Objective-C.

The question is what really made people think Objective-C was difficult – was it the old school syntax, or was its lack of strong static typing? – Because Swift is betting on the latter.

What do you think?

4 thoughts on “An ObjC 3.0 What-If

  1. I think people who never really tried Objective-C thought it was difficult due to the odd syntax. For those who actually tried it I don’t think syntax was an issue. But even as a long term Objective-C fan I’d say something had to change. While Objective-C was pretty good for UI development it was e.g. too cumbersome to write an algorithm that needed to work correctly. Lack of generics, lax typing etc made it difficult to do. But on a similar not I think Swift shows some problems for pure UI style development. When you are putting together losely coupled parts such a strict language as Swift does cause a bit of annoyance. Still overal I think Swift is clearly worth it. I think Swift will mean less bugs and more correct programs.

    Liked by 1 person

  2. For me it was definitely the odd syntax. I think Apple could have done 4 things to make obj-c more palatable for devs.
    1 – adopt dot notation syntax for both methods and properties

    2 – implement better language constructs i.e. static typing, generics, etc

    3 – do away with header files/interface sections and use public, private, internal keywords

    4 – make property declarations more straight-forward. The whole ivar-synthesize thing is a bit confusing. In other languages I can understand property syntax/declarations in about 30 seconds but I’m still confused as to how they work with obj-c (granted my only experience with obj-c is a few intro tutorials)

    But as someone coming from a .Net background these 4 changes would’ve gone an extremely long way in making obj-c easier for me to learn and use.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s