General code structure
Optional semicolons
Semicolons serve the same general purpose in Eero as they do in C/Objective-C. That is, they terminate and separate statements and declarations. However, with rare exception, they’re optional in Eero. As in Python and Ruby, statements generally end with newlines, but the parser will also handle unambiguous statement continuations, such as dangling commas or arithmetic operators, onto subsequent lines. The parser handles other situations as well.
Motivation: readability, DRY
Off-side rule (indentation indicates block scope)
Like Python (and various other languages), Eero adheres to the off-side rule for block scope. The compiler doesn’t dictate the level of indentation, but it must remain consistent for consecutive lines in a particular block. This mechanism completely supplants the use of curly braces as block delimiters.
Motivation: readability, safety, DRY, WYSIWYG
Optional parentheses around conditions
Eero doesn’t require the first level of parentheses around conditional expressions. This applies to if, for, while, switch, catch, etc..
if counter >= 0
counter++
Motivation: readability, DRY
Message passing without square brackets
In Eero, you send messages to objects or classes without using square brackets. As a result, you separate parameters using commas. Furthermore, you can group message-passing expressions with parentheses, just as you can any other expressions.
id myarray = NSMutableArray new
myarray addObject: myobject
myarray replaceObjectAtIndex: 0, withObject: (myobject description)
Motivation: readability
Keywords
Objective-C keywords without ’@’
Eero recognizes Objective-C keywords as regular language keywords. They don’t need to be preceded by an ’@’ character.
interface MyClass : NSObject
end
Motivation: readability
Logical and bitwise operators and, or, not, etc.
Eero recognizes the alternative logical and bitwise operators introduced in C++. (They’re also available to standard C through the inclusion of header iso646.h, introduced to the C90 standard in 1995.)
if ready and initialized
counter++
Motivation: readability
The goto statement is illegal
In Eero, goto is still a recognized keyword but its use is forbidden and will result in a compilation error.
Motivation: safety
Namespaces
Neither C nor Objective-C supports namespaces, relying on unique prefixes to prevent symbol collisions. Eero provides a backward-compatible way of obtaining some of the readability benefits of namespaces.
Built-in symbol search on names with the “NS” prefix
When looking to resolve symbol names, the Eero compiler first checks the name as-is, and if not found, tries again with the “NS” prefix. This means you can use the types, functions, non-macro constants, and so on found throughout the Foundation and Cocoa frameworks without “NS”. For example, anywhere a class name is used, you can use “String” instead of “NSString”.
id mystring = String stringWithUTF8String: "Hello, World"
Motivation: readability
User-defined prefix namespaces
In addition to the built-in “NS” prefix, users can declare their own prefixes to be applied to any scope (file, method, function, etc.). This is done through the using prefix declaration. For example, to find symbol names without providing an explicit “AB” prefix (from the AddressBook framework), use the following:
using prefix AB
...
id theAddressBook = AddressBook sharedAddressBook
Motivation: readability
Variables and literals
Object declarations are assumed to be pointers
Since Objective-C objects are always references (pointers to objects), it is never valid to define object variables as anything but pointers. Instead of issuing an error, Eero implicitly treats all declarations of a class type as a pointer to that type. This means that no associated asterisk is needed.
NSString mystring = myobject description
Motivation: readability, DRY
Local type inference
You can declare and initialize local variables using the Eero-specific ”:=” operator. For example, the following code declares an integer initialized with a value:
i := 100
The const specifier also works with this operator:
const name := "Danielle"
You can still declare variables in the standard C/Objective-C way, using explicit types:
int i = 100
Motivation: readability, DRY, safety (reduces temptation to use types like id)
No variable shadowing
Partly due to the type inference feature, and partly as a general code safety and readability improvement, the compiler does not permit variable shadowing. Attempting to re-declare a variable of the same name in an inner scope will result in a compilation error.
counter := 0
if isReady
counter := 0 // compiler error
Motivation: safety, readability
NSString literals using single quotes
In Eero, you enclose Objective-C/Foundation string literals in single quotes, with no preceding ’@’ symbol:
mynsstring := 'Hello, world'
Motivation: readability
No ’@’ needed for array and dictionary literals
As in Objective-C, you can express array and dictionary literals with square and curly brackets, respectively. However, no ’@’ symbol is needed:
myarray := ['a', 'b', 'c']
mydict := { 'a' : 'A', 'b' : 'B', 'c', 'C' }
Motivation: readability, DRY
Mutable array and dictionary literals
Non-empty array and dictionary literals are immutable. However, unlike Objective-C, empty literals are the mutable versions of their classes, allowing:
mydict : = {}
mydict['a'] = 'A'
Motivation: readability, DRY
Variable definitions using type inference and nil or Nil
If used with a nil or Nil, inferred variable types are of type id or Class, respectively:
myobject := nil // is of type id
myclass := Nil // is of type Class
Motivation: readability, DRY
Selector and protocol literals
While you can still use the selector compiler directive, Eero recognizes selector literals enclosed in vertical bars:
myobject performSelector: |applicationReady:|, withObject: nil, afterDelay: 0
Enclose protocol literals in angle brackets:
if myobject conformsToProtocol: <Coding>
...
Motivation: readability
NSRange literals (and expressions) using ‘..’ or ’…’
You can create range literals with integer values (or variables) separated by an ellipsis, which expresses an (inclusive) sequence stored as an NSRange struct. For example:
myrange := 0 .. 10 // the equivalent of NSMakeRange( 0, 11 ) or (NSRange){ 0, 11 }
Note that Eero interprets both two and three consecutive periods as an ellipsis.
Motivation: readability
NSRange variables and literals in for in loops
You can use variable or literal ranges directly in for-in loops:
for int i in myrange
// perform loop operations
for int i in 0 .. 10
// perform loop operations
Motivation: readability, DRY
Underscores permitted in numeric literals
For improved readability, underscores can appear within numeric literals. The compiler ignores the underscores, which do not modify the value in any way. Underscores cannot be the first or last character of the literal. Some examples:
const TimeInMilliseconds := 30_000 // 30 seconds
const int MinimumBalanceInCents = 100_00 // 100 dollars
const RGBASolidWhite := 0x_FF_FF_FF_FF
const long double Pi = 3.141_592_653_589
Motivation: readability
Stricter enum type checking
Variables of enum types have stronger type checking than in standard C/Objective-C. Without an explicit cast, they can only be assigned values defined for their specific enum type. Since the enum values carry type information, they can also be used with type inference.
enum RGBColors { RED, GREEN, BLUE }
color := RED // inferred to enum type RGBColors
color = GREEN // valid
color = 0 // compiler error
enum CMYKColors { CYAN, MAGENTA, YELLOW, BLACK }
color = MAGENTA // compiler error
Implicit conversion from an enum value to an integer type is still allowed, however:
int value = RED // valid
Classes
Default superclass is NSObject
When you declare a class but don’t specify a superclass, NSObject is assumed.
interface MyClass // superclass is implicitly NSObject
end
Otherwise, subclassing is done in the same way as in standard Objective-C:
interface MyStringSubclass : String
end
In the rare cases where you need to declare a new root class, specify an explicit superclass of void.
interface MyRootClass : void
end
Motivation: readability (via sane defaults)
Method parameter type names are not enclosed in parentheses
In Eero, you do not enclose method parameter types in parentheses. However, like their message-sending counterparts, you do separate parameters with commas.
initWithBytes: const void* bytes,
length: UInteger length,
encoding: StringEncoding encoding
Motivation: readability
Method parameters have default variable names
If omitted, Eero generates argument variable names derived from their selectors. They’re determined by the following rules, listed by most to least commonly encountered:
-
If the method or parameter name contains words separated by camel case, then the last word (scanning left to right), converted entirely to lowercase, is used. For example, method name “initWithString” results in variable name “string.”
-
The first camel-case word encountered that contains two consecutive uppercase characters (scanning left to right) is used, along with all subsequent words, and no character cases are modified. For example, method name “initWithUTF8String” results in variable name “UTF8String.”
-
If no uppercase characters are encountered, the entire method or parameter name is used. For example, parameter name “encoding” results in variable name “encoding.”
-
If the first character in the method or parameter name is uppercase, the entire name is used. For example, method name “CreateNewString” results in variable name “CreateNewString”
initWithBytes: const void*,
length: UInteger,
encoding: StringEncoding
Motivation: readability, DRY
Method return type specified by trailing return, void by default if omitted
To specify a return type, use a comma followed by the return keyword and a type. Unlike Objective-C, if unspecified, there is no return value for the method. This does not affect Foundation, Cocoa, or any other imported legacy headers.
initWithBytes: const void*,
length: UInteger,
encoding: StringEncoding,
return id
Motivation: readability, WYSIWYG
Methods are instance methods by default
Like standard Objective-C, whether a method is an instance or class method is determined by a preceding ‘-’ or ’+’, respectively. However, the ‘-’ is optional for Eero.
interface MyStringClass
+ stringWithUTF8String: const char*, return instancetype
initWithCString: const char*, encoding: StringEncoding, return instancetype
initWithString: String, return instancetype
end
Motivation: readability (via sane defaults)
Optional parameters and default arguments
Eero supports a shorthand notation for optional method parameters and default arguments. The Objective-C way of doing this – defining a set of methods, some of which omit parameters – is still possible, and can be mixed freely with this notation.
There are two distinct parts to the process: flagging the parameters in the interface/protocol as optional, and specifying default values in the implementation.
For the interface, optional parameters are enclosed in square brackets, following a common technical documentation convention:
interface MyClass
openFile String, [withPermissions: String], return FileHandle
end
For the implementation, the default values of the optional parameters are assigned using an ’=’ (equal) after the type or variable name, followed by the default assignment expression:
implementation MyClass
openFile: String, withPermissions: String = 'r', return FileHandle
handle := nil
if permissions == 'r'
handle = FileHandle fileHandleForReadingAtPath: file
else if permissions == 'w' or permissions == 'rw'
handle = FileHandle fileHandleForUpdatingAtPath: file
return handle
end
An added advantage is that the user of the interface should not generally know (or care about) what the default value is; it’s an implementation detail.
Because of this separation of interface and implementation, and like properties’ synthesize, this convenience notation is not required. The individual methods can be constructed manually, like so:
implementation MyClass
openFile: String, withPermissions: String, return FileHandle
...
openFile: String, return FileHandle
...
end
The same holds for interfaces with discrete method declarations and corresponding implementations using the assignment notation. Thus even standard Objective-C interfaces and protocols can benefit.
Optional parameters are restricted to the second and subsequent items. However, required and optional parameters may be alternated. (Thanks to named parameters, there are no C++-like restrictions in this regard.) For example:
interface MyClass
openFile: String, [withPermissions: String], seekToEnd: BOOL, [closeAfterReading: BOOL], return FileHandle
end
Like all other Eero features, this does not introduce any binary breaks. This is just syntactic sugar that generates simple boiler-plate methods “under the hood.”
Motivation: readability, DRY
Default return expression
You can specify a default return expression for a method definition by providing an ’=’ (equal) after the return type, followed by an expression:
openFile: String, withPermissions: String, return FileHandle = nil
if permissions == 'r'
return FileHandle fileHandleForReadingAtPath: file
else if permissions == 'w' or permissions == 'rw'
return = FileHandle fileHandleForUpdatingAtPath: file
If no return statement is encountered in the method body, the method will return the value of this expression. This also allows compact method definitions for getter-like or stubbed methods:
interface MyClass
model, return String = 'G35'
serialNumber, return String = 'X344434AABC'
end
Motivation: readability, DRY
No curly braces needed around member-variable declarations
Member variables, while still requiring placement at the beginning of an interface or implementation, don’t need to be surrounded by curly braces:
interface MyClass
protected
int counter
id delegate
model, return String
serialNumber, return String
end
Motivation: readability, DRY
Declarations of properties of the same type can be grouped together
You can use a single property specification with a group of declarations. The declarations must follow any member variables.
interface MyClass
protected
id delegate
property (assign)
String name
String desc
model, return String
serialNumber, return String
end
Motivation: readability, DRY
Method instancetype parameters
Objective-C supports the use of instancetype for method return types, but not method parameters. Eero removes this restriction, supporting instancetype parameters as well as return types:
protocol OrderedCollection <FastEnumeration>
at: UInteger index, return instancetype
put: instancetype object
end
Motivation: safety
Object operators
Eero supports a limited set of common operators for Objective-C objects. All operands involved must be objects. The operators are, in fact, aliases for certain methods. However, the operators follow the same precedence rules as their primitive-data-type counterparts.
Built-in equality and inequality comparison operators
For all objects, the comparison operators ‘==’ (equals) and ‘!=’ (not equal) map to the isEqual method. The result is negated for ‘!=’. This replaces the low-level address comparison used in standard Objective-C for two objects. This allows valid comparisons of the form:
mystring := MutableString new
mystring appendString: 'Hello, World'
if mystring == 'Hello, World'
// gets here, since above condition is true
You can still perform explicit address comparisons by casting both operands to void* types.
Motivation: readability, WYSIWYG
Built-in ’+’ (binary version) and ’«’ string operators
When the ’+’ binary operator is used, stringByAppendingString will be sent to instances of classes or protocols that respond to it (mainly NSString and its subclasses):
helloString := 'Hello'
worldString := 'World'
helloWorldString := helloString + ', ' + worldString
When the ’«’ binary operator is used, appendString will be sent to instances of classes or protocols that respond to it (mainly NSMutableString):
mystring := ''
mystring << 'Hello, World'
Motivation: readability
Binary operators (effectively, operator overloading)
The following chart shows the supported object binary operators and resulting methods. In the cases of ’+’ and ’«’, defining these methods for a particular class overrides the built-in versions.
| Operator | Method name | Notes |
| + | plus | includes support for += |
| - | minus | includes support for -= |
| * | multipliedBy | includes support for *= |
| / | dividedBy | includes support for /= |
| % | modulo | includes support for %= |
| < | isLess | |
| > | isGreater | |
| <= | isGreater | result is !(left isGreater: right) |
| >= | isLess | result is !(left isLess: right) |
| << | shiftLeft | |
| >> | shiftRight |
Motivation: readability
Array and dictionary subscript operators with integer and object indexes
Eero supports the same array and dictionary object subscript operators (‘[]’) found in Objective-C. For platforms that support them, these operators acts as aliases for methods IndexedSubscript, KeyedSubscript, etc.. However, on systems with older versions of the Foundation library that are missing this family of methods, Eero falls back to objectAtIndex, objectForKey, etc..
Motivation: readability, backward compatibility
Object subscript operators with NSRange indexes
Objects indexed using ‘[NSRange]’ that are derived from classes or protocols that respond to method substringWithRange (mainly NSString and its subclasses) will be sent that message. This allows concise string slices:
mystring := 'Hello, World'
worldString := mystring[7 .. 11]
All other object types indexed using ‘[NSRange]’ will be sent subarrayWithRange (found in NSArray and its subclasses). This allows concise array slices:
myarray := ['a', 'b', 'c', 'd', 'e', 'f']
abcRange := 0 .. 2
abcSubarray := myarray[abcRange]
Motivation: readability
Special object casts (boxing and unboxing)
Note: Although not directly related to these conversions, casting an object variable to a class (without the trailing ”*”) simply casts the object to the target class type. This is in line with Eero’s general class-name policy:
mystring := (String) someObject // same as "(String*) someObject"
Converting primitive data types to objects (boxing)
“Casting” a primitive data type to an Objective-C class results in the creation of an object of that type (boxing), implicitly using the appropriate method to do so. For example:
unsigned int age = 25
ageAsNumberObject := (Number) age // same as (Number numberWithUnsignedInt: age)
array := [ (Number) 1.0, (Number) 2.0, (Number) 3.0 ] // same as (Number numberWithDouble: x)
This works with any class that implements the appropriate numberWith<Type> class methods.
C-style character strings are also supported, implicitly using class method stringWithUTF8String:
const char* helloworld = "Hello, World"
stringObject := (String) helloworld
mutableStringObject := (MutableString) helloworld
There are no implicit conversions from primitive to object.
Motivation: readability, DRY
Converting objects to primitive data types (unboxing)
“Casting” an Objective-C object to a primitive data type results in a value extraction (unboxing), implicitly using the appropriate method to do so:
age := (unsigned int) ageAsNumberObject
Methods intValue, unsignedIntValue, doubleValue, etc. are used, as determined by the cast type. These messages are sent to objects of any type, so any class that supports these methods (not just NSNumber and its subclasses) can take advantage of them. For example, since NSString supports floatValue, this works:
pi := (float) '3.14159'
Conversion of NSString objects to C strings (technically, UTF8 strings) is also supported, sending message UTF8String to the source object:
helloworld := (const char*) 'Hello World'
FILE* file = fopen( (const char*)fileName, "r" )
(Please see NSString Class Reference for details on memory management of the returned C string).
Motivation: readability, DRY
Blocks enhancements
Blocks are generally unchanged from Apple’s C/Objective-C implementation. The primary difference in Eero is the use of indentation instead of braces to enclose them. A newline must immediately follow the parameter declaration list:
myblock := ^(int x, int y)
if x < 0
printf( "value was negative! (%d)\n", x )
x = 0
return x + y
Compact blocks
Eero supports a compact form for blocks that only requires a single expression or return statement. Within the parentheses following the ’^’ (caret), a vertical bar separates the parameters from the expression or return statement. Aside from a return, no statements are allowed, so condition statements and loops cannot be present.
xyblock := ^(int x, int y | return x + y)
vblock := ^(int* value | *value = 0)
descriptions := mylist mapWith: ^(id element | return element description)
Motivation: readability
Nested functions (const blocks)
Eero supports constructions that look and act like nested functions but are semantically const blocks. These can be defined in methods, functions, or other blocks, and the normal block-closure rules apply. They can also be defined within if, else, while, for, etc. indented bodies. It is possible to call them directly, like a normal function, or pass them as block arguments.
implementation MyClass
processMutableStrings: Array
void addPositionSuffix( id obj, UInteger idx, BOOL* stop )
if obj == ''
*stop = YES
else
obj appendFormat '%u', idx
strings enumerateObjectsUsingBlock: addPositionSuffix
addPositionSuffix = nil // generates a compiler error: addPositionSuffix is a const
end
Motivation: readability
The switch statement
While being very similar in structure (and the same in performance) to the switch statement in the C language family, Eero makes various changes to the construct.
No fall-through
Eero case statements do not fall through, and thus do not require break statements. The ’:’ (colon) following the case value is optional, but the newline and indented code block is not. (The usual off-side rules apply.) Furthermore, these blocks automatically form declaration scopes, allowing declarations and definitions local to these scopes.
switch color
case kRed
colorName = 'red'
case kGreen
colorName = 'green'
case kBlue
colorName = 'blue'
case kAlpha
; // do nothing
default
colorName = 'unknown'
The loss of convenience due to the absence of fall-through is compensated for by the safer mechanisms described in the next two sections.
Motivation: safety, readability
Comma-separated lists of cases
Cases can be grouped together as comma-separated lists of values:
switch color
case kRed, kGreen, kBlue
colorIsValid = YES
default
colorIsValid = NO
These can be freely mixed with single and ranged case values within the same switch statement.
Motivation: safety, readability
Case-value ranges
You can specify a set of case values using an ellipsis between the first and last items in a consecutive range of values:
enum Colors { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }
switch color
case RED .. VIOLET
colorIsValid = YES
default
colorIsValid = NO
These can be freely mixed with single and comma-separated list values within the same switch statement.
Motivation: safety, readability
Loop enhancements
for-in loop indexing
Eero introduces a convenient and compact way to get a loop index in a for-in loop. This is similar to Python’s for-in-enumerate construct. Index counting always starts at zero.
The syntax for the feature is an integral variable declaration or expression preceding the variable-in-range component, the two sections separated by a colon:
for int index : id obj in myEnumerableObject
Log('Index: %d', index) // index will count from 0 to n loops
Log('Object: %@', obj)
int idx
for idx : int i in 1 .. 10
Log('Index: %d', idx) // idx will count from 0 to 9
As demonstrated, the integral variable holding the index value can either be a pre-existing variable (defined before and outside of the loop) or declared and valid only within the loop.
This feature is supported for all types of for-in loops: fast enumeration objects, NSRanges, and when the C++ interoperability pragma is in effect, C++11 for-range loops.
Motivation: DRY
The preprocessor
Direct #import of Eero and legacy (Objective-C and C) headers
Eero code can directly import and use Eero and standard Objective-C and C header files. This is done through the Objective-C #import and #include preprocessor directives.
When the filename is enclosed in single quotes, the imported/included file is treated as an Eero file. When either angle brackets or double quotes are used, the file is treated as standard Objective-C or C.
#import <Foundation/Foundation.h>
#import "my_objc_header.h"
#import "my_c_header.h"
#import 'my_eero_header.h'
System header files, regardless of the type of filename string literal used, are always treated as as C/Objective-C. (Please see the Clang Compiler User’s Manual for more details on system headers).
Motivation: backward compatibility
C++ interoperability #pragma
You can directly use C++ APIs and libraries via an Eero-specfic compiler pragma:
#pragma eero "C++"
#import <vector>
...
std::vector<int> v
v.push_back(10)
v.push_back(20)
Please see the Eero wiki for more details.
Motivation: interoperability
Reserved symbols for future features
-
The symbol import is an unused but reserved keyword, in anticipation of the @import feature, which will likely be introduced by Apple in the future.
-
’=>’ is an unused but reserved operator symbol.