The previous post deliberately did little aside from swapping the syntax for a Swift-like one.
This time, we’re going to look at what features we could add while retaining 100% backward compatibility with Objective C 2.0.
I will be borrowing heavily from the languages mentioned previously – please check out the languages for a deeper understanding of the features, as this will be a brief overview only.
I’m going to take the liberty of referring to this future language as “ObjC 3.0” throughout, and it should be understood as “what an imaginary ObjC 3.0 could have looked like if they decided to change the syntax by a lot”. It is not speculation over what the actual next iteration of Objective-C from Apple will look like.
That said, I’ll label examples as ObjC 2.0 (current) and ObjC 3.0 (future imaginary ObjC with lots of syntax changes), to give you an idea of what the feature does.
1. Structs (and enums) with dot-invocation syntax.
All functions prefixed with a struct name, and taking that struct as it’s first name, will be callable by dot-invocation:
// ObjC 2.0
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
// ObjC 3.0
context.drawImage(CGRectMake(0, 0, 145, 15), image.CGImage);
// Adding a new CGContext method:
// ObjC 2.0 (also valid in ObjC 3.0)
void CGContextDrawImageWithMaskInRect(CGimageRef image,
CGImageRef mask,
CGRect rect) { ... }
// ObjC 3.0
extension CGContext
{
void drawImageWithMaskInRect(CGImage image,
CGImage mask,
CGRect rect) { ... }
}
Motivation: Simplifies code-completion, allows grouping of related functions.
2. Automatic lookup for NS-classes and user defined prefix lookups
// ObjC 2.0 NSArray *array = @[@"Foo", @"Bar"]; // ObjC 3.0 Array *array = ["Foo", "Bar"]; // ObjC 2.0 ABAddressBook *addressBook = [ABAddressBook sharedAddressBook]; // ObjC 3.0 using prefix AB; ... AddressBook addressBook = AddressBook.sharedAddressBook;
Motivation: Readability.
3. Extendable compile time auto-boxing / unboxing
// ObjC 2.0
int value = [@"123" intValue];
NSNumber *number = @(123);
double foo = 20.0f + [fooString intValue];
// ObjC 3.0
conversion int
{
case NSString: return value.intValue;
}
conversion NSNumber
{
case int: return NSNumber.numberWithInt(value);
}
...
int value = "123";
NSNumber number = 123;
double foo = 20.0f + (int)fooString;
Motivation: Readability, DRY.
4. Range literals
// ObjC 2.0
for (int i = 0; i < 10; i++) { ... }
NSRange range = NSMakeRange(0, 3);
for (int i = range.location; i < range.location + range.length; i++) { ... }
// ObjC 3.0
for (int i in 0..9) { ... }
Range range = 0 .. 2;
for (int i in range) { ... }
Motivation: Readability, DRY
5. Limited set of operators translated to method/function signatures
// ObjC 2.0
CGPointAddCGPoint(CGPoint point1, CGPoint 2);
CGPoint point3 = CGPointAddCGPoint(point1, point2);
// ObjC 3.0
extension CGPoint
{
plusCGPoint(CGPoint point) {
return CGPointMake(self.x + point.x, self.y + point.y);
}
}
CGPoint point3 = point1 + point2; // + invokes "<Type>Plus<Type>" function
// ObjC 2.0
NSString *foo = @"foo";
NSString *bar = @"bar";
NSString *foobar = [foo stringByAppendingString:bar];
// ObjC 3.0
extension String
{
String plusNSString(String string) {
return self.stringByAppendingString(string);
}
}
String foo = "foo";
String bar = "bar";
String foobar = foo + bar; // + invokes "plus<Type>:" selector
Motivation: Readability, Ease-of-use
6. Built-in higher order messaging
Read more about HOM here.
// ObjC 2.0
NSMutableArray *result = [NSMutableArray array];
for (Employee *employee in array) {
if ([employee isPaidSalary:1000]) {
[result addObject:[object stringValue]];
}
}
// ObjC 3.0
Array result = array.select().isPaidSalary(1000);
Motivation: Readability, DRY
7. Complex global and class constants
// ObjC 2.0
+ (instancetype)sharedInstance {
static dispatch_once_t *predicate;
static MyClass *instance = nil;
dispatch_once(&predicate, ^{
instance = [MyClass new];
}
return instance;
}
// ObjC 3.0
static MyClass instance = MyClass.new();
Motivation: Safety, Readability, DRY
8. Simplified function / block declaration syntax
// ObjC 2.0
void (^foo)(NSString *string); // Block
void (*bar)(NSString *string); // Function pointer
- (void)method:(BOOL(^)(NSNumber *))block { ... }
// ObjC 3.0
{NSString -> void} foo; // Block
(NSString -> void) bar; // Function pointer
void method({Number -> BOOL} block) { ... }
Motivation: Readability
9. Associated values for enums
// ObjC 2.0
typedef enum _Foobar { Foo, Bar, Baz } Foobar;
static int FoobarValues[] = { -1, 1, 100 };
static NSString *FoobarNames[] = { @"Foo", @"Bar", @"Baz" };
NSString *FoobarToString(Foobar foobar) {
return FoobarNames[foobar];
}
int FoobarValue(Foobar foobar) {
return FoobarValues[foobar];
}
...
Foobar foo = Foo;
FoobarValue(foo); //=> -1
FoobarToString(foo); // => @"Foo"
// ObjC 3.0
enum Foobar {
int value;
Foo(-1),
Bar(1),
Baz(100)
}
...
Foobar foo = Foo;
foo.value; // => -1
foo.stringValue; // => "Foo" (autogenerated from source name)
Motivation: Readability, DRY, Ease-of-use
10. Easy iteration over enums
// ObjC 2.0
typedef enum _Foobar { Foo, Bar, Baz } Foobar;
static Foobar FoobarAllValues[] = { Foo, Bar, Baz };
static int FoobarValueCount = 3;
...
for (int i = 0; i < FoobarValueCount; i++) {
Foobar foobar = FoobarAllValues[i];
...
}
// ObjC 3.0
enum Foobar { Foo, Bar, Baz };
for (Foobar foobar in Foobar.all) {
...
}
Motivation: Readability, DRY
11. Safe untyped dispatch
// ObjC 2.0 if ([foo respondsToSelector:@selector(bar)]) [foo bar]; // ObjC 3.0 (Using Higher Order Messaging) foo.ifResponds().bar();
Motivation: Readability, DRY, Safety
Can all of these features really be added?
Short answer: sort of.
Taken together, there are sure to be problems with the specific syntax choices made. There is an overwhelming chance that there are ambiguities if someone would try to implement the features above exactly as they are written, however – the particular syntactic choices are typically to highlight the feature, rather to be the final word in how the language should look.
That said, with a few tweaks they all could be made to work together.
SHOULD all of these features be added?
Probably not, but many of them address very real weaknesses in ObjC. This list is actually just a sample to get the ideas flowing, and to illustrate how some of the hurdles with ObjC 2.0 can be overcome by a successor that breaks syntax with the past, but still retains full backward compatibility.
This looks like a completely different language, how can it be backwards compatible?
The language will have its own header format which will compile to standard ObjC .h files that can be included using #import <objc30class.h>, so your ObjC 2.0 classes can happily ignore the fact that the header’s originally written in a syntactic variant.
Since all ObjC 3.0 code compiles down to “primitives” that ObjC/C can understand, there is full interoperability both ways. This is different from Swift, which cannot bridge its structs, enums and generics.
What about header files, shouldn’t you do away with those too?
Removing the header files is quite possible, but there are some real life advantages to declaring the interface and implementation separately. Moreover, if we implement a subset of the features above, we would still be able to use the normal ObjC 2.0 headers, which would remove the need to generate compatibility headers.
Weighing pros and cons of header files will have to wait for some future blog entry.
Discuss this on Hacker News.
You should read “ The problematic culture of «Worse is Better»”: http://pchiusano.github.io/2014-10-13/worseisworse.html
LikeLike
How do you feel this relates to the subject at hand?
LikeLike