#import <Foundation/Foundation.h>

int main()
  parts := ['hello', 'world']
  greeting := ''
  for String part in parts
    if part == parts[0]
      Locale myLocale = Locale.currentLocale
      greeting << part.capitalizedString
    else
      greeting << ' '
      greeting << part

  Log('%@', greeting + '!')
  return 0


На днях из любопытства решил посмотреть, на какой стадии находится проект Eero — диалект Objective-C с альтернативным легким синтаксисом. Оказалось, что проделан уже большой фронт работ и Eero представляет из себя очень интересную разработку.

Пример кода


Для затравки традиционное сравнение небольшого участка кода на Eero и на Objective-C.
Objective-C
#import <Foundation/Foundation.h>

@interface FileHelper : NSObject

@property (readonly) NSString* name;
@property (readonly) NSString* format;

-(NSFileHandle*) openFile: (NSString*) path;

-(NSFileHandle*) openFile: (NSString*) path
          withPermissions: (NSString*) permissions;

@end


@implementation FileHelper

-(NSString*) name {
  return @"Macintosh";
}

-(NSString*) format {
  return @"HFS+";
}

-(NSFileHandle*) openFile: (NSString*) path {
  return [NSFileHandle fileHandleForReadingAtPath: path];
}

-(NSFileHandle*) openFile: (NSString*) path
          withPermissions: (NSString*) permissions {

  NSFileHandle* handle = nil;

  if ([permissions isEqualTo: @"readonly"] || [permissions isEqualTo: @"r"]) {
    handle = [NSFileHandle fileHandleForReadingAtPath: path];

  } else if ([permissions isEqualTo: @"readwrite"] || [permissions isEqualTo: @"rw"]) {
    handle = [NSFileHandle fileHandleForUpdatingAtPath: path];
  }

  return handle;
}

@end

Eero
#import <Foundation/Foundation.h>

interface FileHelper

  String name   {readonly}
  String format {readonly}

  openFile: String, [withPermissions: String], return FileHandle

end


implementation FileHelper

  name,   return String = 'Macintosh'
  format, return String = 'HFS+'

  openFile: String path, withPermissions: String = 'readonly', return FileHandle = nil

    if permissions == 'readonly' or permissions == 'r'
      return FileHandle.fileHandleForReadingAtPath: path

    else if permissions == 'readwrite' or permissions == 'rw'
      return FileHandle.fileHandleForUpdatingAtPath: path

end


Введение


Eero это полностью интерфейсно и бинарно совместимый диалект Objective-C. Реализован с помощью форка LLVM/clang компилятора, т.е. генерирует бинарный код напрямую, благодаря чему с ним работают стандартные инструменты разработки LLVM/Clang, включая отладчик, статический анализатор и прочие. Также заявлена экспериментальная конвертация Eero кода в стандартный Objective-C код 1.

В Eero можно напрямую использовать все Cocoa-фреймворки и любой Objective-C/C++ код, достаточно лишь подключить нужные заголовочные файлы. Также легок и обратный процесс: если для Eero-класса интерфейс определить на Objective-C, то его можно использовать напрямую, бинарные файлы полностью совместимы.

По существу, Eero является мощным синтаксическим сахаром для Objective-C, повышая читаемость, безопасность и сокращая объём кода. По словам автора (Andy Arvanitis), изначально он всего лишь хотел избавиться от скобочек в Objective-C, а название языка выбрал, когда увидел модель стола-постамента, который успешно выполнял свои функции без лишних мешающихся ножек. Итак, язык назван в честь финского архитектора стола Eero Saarinen. Я не силён в финском, но судя по всему, название должно звучать примерно как «эро» с ударением на первый слог.

Синтаксис


Документация написана качественно и легко читается, для каждой особенности языка указывается мотивация, почему решили сделать именно так, в основном это: читаемость, безопасноть, DRY, WYSIWYG. Интересных деталей в языке много, я не буду рассматривать все, а ограничусь небольшим субъективным списком.

Во-первых, в Eero для выделения блоков вместо фигурных скобок используются отступы, как в Python. Точка с запятой в конце инструкций и определений не обязательна.
int count = 0
while ( count < 100 )
  something = false
  count++
  i++; j++


В Eero отправка сообщений записывается через нотацию с точкой, для сообщений без аргументов это аналогично нотации для свойств (property). Аргументы передаются после двоеточия, несколько аргументов разделяются запятыми.
id myarray = NSMutableArray.new
myarray.addObject: myobject
myarray.insertObject: myobject, atIndex: 0


Поскольку в Objective-C нельзя представить объект никак иначе, кроме как через указатель, в Eero все переменные классовых типов обрабатываются как указатели, без использования звездочки (*).
NSString mystring = myobject.description
NSString otherString = (NSString) someObject


При декларации локальных переменных можно не указывать их тип, он будет автоматически выведен из присваиваемого выражения. Для этого используется специальный оператор ":=".
i := 100
mystring := myobject.description


В Eero реализована такая замечательная вещь, как пространства имён (namespaces). Префикс «NS» подключен по-умолчанию, т.е. все типы из фреймворка Foundation можно использовать без префикса.
mystring := String.stringWithUTF8String: "Hello, World"

Компилятор сначала проверяет имена сущностей в том виде, как они указаны в коде, и, если имя не найдено, добавляет зарегистрированные префиксы. Декларация дополнительных префиксов осуществляется с помощью директивы using prefix.
using prefix AB
...
theAddressBook := AddressBook.sharedAddressBook

Однако в текущей реализации есть существенный недостаток: возникновение коллизий в результате опущения префиксов никак не отслеживается, просто используется первый подходящий вариант:
void AAPrint(String str)
  NSLog('AA: %@', str);

void BBPrint(String str)
  NSLog('BB: %@', str);

using prefix AA
using prefix BB
...
Print('test') // AA: test


В Eero не обязательно указывать имена аргументов метода, в случае опущения, они автоматически определяются из селектора. Например, в следующем методе аргументы получат имена bytes, length и encoding:
initWithBytes: const void*,
       length: UInteger,
     encoding: StringEncoding

Тип возвращаемого значения указывается после аргументов, по умолчанию используется void.
initWithBytes: const void*,
       length: UInteger,
     encoding: StringEncoding,
        return id

В Objective-C в случае, когда у метода есть опциональные параметры, принято определять несколько методов с разным количеством аргументов. В Eero можно декларировать методы с необязательными параметрами.
interface MyClass
  openFile String, [withPermissions: String], return FileHandle
end

В имплементации дефолтное значение опционального аргумента обозначается с помощью знака "=".
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

Можно указать дефолтное возвращаемое значение, что например позволяет определять очень компактные геттеры:
implementation MyClass 
  model, return String = 'G35'
  serialNumber, return String = 'X344434AABC'
end


Свойства декларируются очень просто. Атрибуты при необходимости задаются в фигурных скобках.
interface MyClass
  String name {nonatomic, copy}
  String desc {readonly}
end


При определении блоков используются отступы, и не используется знак каретки "^". По сравнению с синтаксисом Objective-C, блоки выглядят очень просто:
myblock := (int x, int y)
  if x < 0
    printf( "value was negative! (%d)\n", x )
    x = 0
  return x + y

Также поддерживается компактная форма записи для простых блоков, состоящих только из возвращаемого выражения:
xyblock := (int x, int y | return x + y)

descriptions := mylist.mapWith: (id element | return element.description)


В Eero реализована перегрузка операторов. Во-первых, для всех объектов оператор "==" является алиасом для метода isEqual. Что актуально в первую очередь для читабельного и безопасного сравнения строк:
mystring := MutableString.new
mystring.appendString: 'Hello, World'

if mystring == 'Hello, World'
  // попадаем сюда, т.к. выражение выше истинно

При использовании бинарного оператора "+" сообщение stringByAppendingString будет послано всем объектам, которые могут на него ответить (преимущественно NSString и его субклассы):
helloString := 'Hello'
worldString := 'World'

helloWorldString := helloString + ', ' + worldString

Аналогично оператор "<<" посылает сообщение appendString:
mystring := ''
mystring << 'Hello, World'

Для своих классов можно перегружать следующие операторы:
Оператор Селектор Примечание
+ plus: Включая оператор +=
- minus: Включая оператор -=
* multipliedBy: Включая оператор *=
/ dividedBy: Включая оператор /=
% modulo: Включая оператор %=
< isLessThan:
<= isLessThanOrEqualTo:
> isGreaterThan:
>= isGreaterThanOrEqualTo:
<< shiftLeft:
>> shiftRight:

Ну, и наконец, в языке на уровне компилятора запрещен goto. :)


Установка и использование


Данный раздел написан по опыту использования Eero в сборке 2013-12-08, XCode 5.0.2 и Mac OS X 10.8.5.

Ставится Eero очень просто. Достаточно скачать и установить плагин для XCode, который уже содержит сборку форка компилятора LLVM. При установке также добавятся шаблоны XCode для создания новых .eero и .eeh файлов. После установки достаточно перезапустить XCode и всё, — можно приступать к работе.

Работает подсветка синтаксиса, автодополнение. Однако, не работает выпадающий список с навигацией по классу, вместо него статичная надпись «No Selection».

Точнее говоря, XCode не видит классы и методы, определенные в файле, но можно воспользоваться инструкциями #pragma mark ..., — они в выпадающем списке видны.

Проверил работу отладчика (он же debugger), всё в порядке. Однако, к сожалению XCode падал при попытке просмотра quick help к выделенному коду. Ещё не всегда корректно работают авто-отступы, но это совсем мелочь. Больше проблем не обнаружено.

Заключение


Лично на меня Eero произвел самое приятное впечатление. Да, язык достаточно молод (домен eerolanguage.org зарегистрирован в январе 2011, а на гитхабе проект появился в декабре того же года), и потому присутствуют недостатки, характерные для данной стадии развития (в основном с интеграцией с IDE). Однако, неразрешимих и критичных проблем не видно, а потому у меня появилось стойкое желание попробовать его в будущих проектах.

Ссылки





1) На данный момент функция конвертирования Eero -> Objective-C сыровата, у меня удачно отработала только на элементарных примерах.

Комментарии (63)


  1. Makito
    16.12.2013 10:14

    Увидев ":=" я сразу вспомнил Pascal и Delphi… Эххх были времена… :)


    1. Xlab
      16.12.2013 10:14

      А теперь ещё и в Go.


  1. KELiON
    16.12.2013 10:14

    Во-первых, в Eero для выделения блоков вместо фигурных скобок используются отступы, как в Python или Ruby.


    Это какие-то неправильные руби


    1. Yan169 Автор
      16.12.2013 10:14

      И правда, что-то меня переклинило, спасибо. :)


  1. Krypt
    16.12.2013 10:14

    Диспозиция:

    @interface TestObject : NSObject
    {
        int test;
    }
    @property (nonatomic, assign, getter = getTest) int test;
    @end
    
    @implementation TestObject
    @synthesize test;
    
    - (id) init
    {
        if (self = [super init])
        {
            NSLog(@"test %d %d", self->test, self.test);
        }
        return self;
    }
    
    - (int) getTest
    {
        NSLog(@"getter");
        return 1;
    }
    @end


    Вывод:
    2013-12-16 13:17:24.637 Test[1224:907] getter
    2013-12-16 13:17:24.815 Test[1224:907] test 0 1

    Вопрос:
    Как это повторить?

    P.S.: Я знаю, что так делать не стоит, но раз вы предлагаете Eero как замену Objective-C — он так тоже должен уметь.


    1. Yan169 Автор
      16.12.2013 10:14

      Примерно так:

      interface TestObject
        int test {nonatomic}
      end
      
      implementation TestObject
        int _test
      
        init, return instancetype = self
          if (self = super.init)
            Log('test %d %d', _test, self.test);
      
        test, return int = 1
          Log('getter');
      
      end
      

      Но по философии языка он должен выполнять те же функции, что Objective-C, но не являться его копией, взяв лучшее, и выкинув худшее. Поэтому далеко не все трюки можно перенести (особенно нежелательные в использовании). Например, в Eero запрещено затенять переменные (shadowed variables):
      counter := 0
      if isReady
         counter := 0 // compiler error
      


      1. Krypt
        16.12.2013 10:14

        Собственно в этом коже getter не нужен даже, надо только функцию в - (int) test переименовать.

        > Например, в Eero запрещено затенять переменные (shadowed variables)
        А можноо про это поподробне?
        То есть я не смогу сделать так

        BOOL status = NO;
        if (!status && <длинное условие>)
            status = YES;
        if (!status && <второе длинное условие>)
            status = YES;
        if (!status && <третье длинное условие>)
            status = YES;
        


        ?

        Правда надо признать, более красивый код я придумать не смог :)
        Навеяно вот этим кодом: pastebin.com/csQmX1BS


        1. alhimik45
          16.12.2013 10:14

          Нет, это что-то типа (пример фигня, конечно):

          BOOL status = NO;
          if(cond){
          BOOL status = YES;
          ...
          }
          


          Хотя иногда, например в лямбде, бывает удобно назвать переменную так же, как и снаружи, ибо другого нормального имени не придумаешь (пишу про CoffeeScript, меня там это пару раз доставало)


          1. Yan169 Автор
            16.12.2013 10:14

            Да, иногда это бывает полезно. С другой стороны, защищает от потенциальных ошибок. Чисто субъективно, я бы предпочел вариант с warning, а не с compile error.


            1. potan
              16.12.2013 10:14

              У меня как раз наоборот — ошибки лезут когда приходится создавать новую переменную, а обращаюсь случайно к старой.
              Правда это специфично для Haskell. Там нельзя написать

              let n = n+1 in ...
              надо давать новое имя
              let n1 = n+1 in ...

              В ML можно, в Haskell придумал замену
              case n+1 in n -> ...
              но уж больно многословно получается.


        1. Yan169 Автор
          16.12.2013 10:14

          Нет, в вашем коде используется одна и та же переменная, так естественно можно писать. Запрещено в локальной области видимости определять новую переменную с тем же именем, что и в родительской. В Objective-С/C так делать можно (можно настроить, чтобы выдавался warning).

          BOOL status = NO;
          if (...){
              BOOL status = YES;
          }
          


  1. rule
    16.12.2013 10:14

    Забавно, как побаловаться.
    Но пока шило на мыло помоему.
    В любом случае многие очень сильные проекты начинались именно вот с такого вот рода экспериментов.


  1. Triang3l
    16.12.2013 10:14

    Какая-то хипстерота. Читать невозможно. Функции как списки переменных выглядят.


  1. KysokZla
    16.12.2013 10:14

    Вообще непонятно зачем? У всех языков свой синтаксис, странным он кажется только тем людям, которые его просто видят в первый раз, если человеку необходим этот язык он начнет его учить и через уже месяц не будет никаких проблем, а, возможно, он даже ему понравится намного больше чем остальные, тут есть множество своих плюсов, как и в любом другом языке. Давайте для Java создадим тоже свой Ero с блекджеком и щлюхами, я подозреваю, что для людей, программирующих на ассемблере или Pascale Java тоже кажется с очень странным синтаксисом.


    1. Yan169 Автор
      16.12.2013 10:14

      На вопрос «зачем?» автор отвечает: «для читаемости, т.к. код читается гораздо чаще, чем пишется».
      Вообще, я согласен, что привыкаешь ко всему, Objective-C лично я воспринимаю абсолютно нормально, особенно после 5 лет разработки. :)
      Но объективно говоря, код на Eero получается куда лаконичнее, плюс дополнительные функции, такие как опциональные аргументы, перегрузка операторов, полная поддержка instancetype, итераторы типа for int i in 0 .. 10 и прочее. Т.е. дело не только в синтаксисе.


      1. Krypt
        16.12.2013 10:14

        Ну если бы не другой синтаксис — возможно, я бы его даже попробовал. А так… Нет, я останусь на Obj-C :)


        1. Yan169 Автор
          16.12.2013 10:14

          Мне Objective-C нравится: философией, рантаймом. Но от синтаксиса я не в восторге, хотя и привык. По-моему, если подходить непредвзято, и забыть про привычки, то можно объективно сказать, что синтаксис Objective-C далек от совершенства: для тех же самых функций можно было придумать куда более удобную запись.


          1. Krypt
            16.12.2013 10:14

            Код Obj-C, раза в 1.5-2 длиннее по сравнению с тем же C#, это да. Но по сравнению с C++ я выигрыша не вижу. Причём если в Obj-C однозначно, то в случае с С++ — кто как хочет, тот так и городит. Так что я бы на синтаксис Obj-C не особо жаловался :)


    1. k12th
      16.12.2013 10:14

      Groovy, Scala, Clojure…


    1. potan
      16.12.2013 10:14

      И у этого языка свой синтаксис, при обеспечении совместимости по библиотекам. Что плохого то?


  1. artyfarty
    16.12.2013 10:14

    Нам не нравится непонятный синтаксис обжСи, поэтому мы сделали свой непонятный синтаксис! Теперь непонятных синтаксисов два.

    При всем моём неприятии синтаксиса обж си,

    name,   return String = 'Macintosh'
    format, return String = 'HFS+'
    

    в оригинале куда понятнее. И что за мода вообще, делать эти руби/питон стайл диалекты языкам…


  1. Goodkat
    16.12.2013 10:14

    Лучше бы они старый добрый и всем понятный C++-синтаксис для Obj-C сделали, если хотели избавиться от вложенных квадратных скобок при обращении к методам.

    А Python-стиль для C — это как-то неправильно совсем запутывает, имхо.


    1. Yan169 Автор
      16.12.2013 10:14

      В C++ нет именованных параметров, поэтому его синтаксис никак не применить.


      1. Goodkat
        16.12.2013 10:14

        Это даже не именованные параметры, а имя метода состоящее из нескольких слов перемежающихся параметрами.
        Прелесть именованных параметров в том, что их можно писать в произвольном порядке.


        1. Zelgadis
          16.12.2013 10:14

          Так сигнатуры методов это же божественная часть Objective C! Не понимаю людей которым ObjC не нравится.


  1. PapaBubaDiop
    16.12.2013 10:14

    Хотя мое мнение никого не интересует, но скажу

    Мне очень нравятся квадратные скобки

    Они напоминают сказочный сюжет про сундук. В сундуке — медведь. В медведе — утка. В утке яйцо. В яйце — игла. На конце иглы — смерть.
    int death = [NSBox boxWithBear:[NSBear bearWithDuck:[NSDuck duckWithEggs:[NSEgg eggWithNiddle:[NSNiddle alloc] init]]]];
    


    1. Agent_Smith
      16.12.2013 10:14

      Да, так намного лучше:

      int death = NSBox.boxWithBear(NSBear.bearWithDuck(NSDuck.duckWithEggs(NSEgg.eggWithNiddle(new NSNiddle))));
      


    1. Goodkat
      16.12.2013 10:14

      [NSNiddle niddleWithADeath:[[NSDeath alloc] init] atTheEnd:theEnd]


      1. PapaBubaDiop
        16.12.2013 10:14

        Ваш пример лучше. В моем умышленная ошибка, никто не поправил.


        1. Goodkat
          16.12.2013 10:14

          Собственно, я не поправил, а расширил создание объекта иглы — остальное было лень набирать.
          А у вас да, ошибка, в death будет не смерть, а сундук :)


          1. Yan169 Автор
            16.12.2013 10:14

            Ошибка в части

            [NSEgg eggWithNiddle:[NSNiddle alloc] init]
            

            Никто не поправил очевидно потому, что в скобочках таки легко запутаться. :)


            1. Krypt
              16.12.2013 10:14

              Зачем замарачиваться скобками, если их компилятор проверит ;)


              1. Yan169 Автор
                16.12.2013 10:14

                Чтобы не было ситуации «чукча не читатель, чукча писатель». Код читается намного чаще, чем пишется.

                Кроме того, компилятор не всесилен. Предположим, мы решили понизить связанность классов и сделать их более универсальными, так, чтобы в одной сущности могло находиться по 2 других:

                @protocol NSContaining <NSObject>
                -(instancetype)initWith:(id)content;
                -(instancetype)initWith:(id)content and:(id)anotherContent;
                @end
                
                @interface NSBear : NSObject <NSContaining> @end
                @interface NSDuck : NSObject <NSContaining> @end
                @interface NSEgg : NSObject <NSContaining> @end
                


                Слабо сходу понять, чем отличаются следующие синтаксически верные варианты:
                [[NSBear alloc] initWith:[[NSDuck alloc] initWith:[[NSEgg alloc] initWith:[[NSNeedle alloc] init]]] and:[[NSEgg alloc] init]];
                
                [[NSBear alloc] initWith:[[NSDuck alloc] initWith:[[NSEgg alloc] initWith:[[NSNeedle alloc] init]] and:[[NSEgg alloc] init]]];
                
                [[NSBear alloc] initWith:[[NSDuck alloc] initWith:[[NSEgg alloc] initWith:[[NSNeedle alloc] init] and:[[NSEgg alloc] init]]]];
                

                Если просто убрать лишние скобки, оставаясь в рамках Objective-C, и воспользоваться нотацией через точку, то уже намного понятнее:
                [NSBear.alloc initWith:[NSDuck.alloc initWith:[NSEgg.alloc initWith:NSNeedle.alloc.init]] and:NSEgg.alloc.init];
                
                [NSBear.alloc initWith:[NSDuck.alloc initWith:[NSEgg.alloc initWith:NSNeedle.alloc.init] and:NSEgg.alloc.init]];
                
                [NSBear.alloc initWith:[NSDuck.alloc initWith:[NSEgg.alloc initWith:NSNeedle.alloc.init and:NSEgg.alloc.init]]];
                


                На практике «скобочный ад» тоже иногда бывает, например при использовании fluent-интерфейсов, при использовании ReactiveCocoa. Т.е. не «ад», конечно, но «ещё чуть-чуть, и будет ад».

                В общем, Readability matters!


                1. Krypt
                  16.12.2013 10:14

                  За такой код руки отрывать надо.
                  Во-первых, можно сразу бить в табло за кривые имена параметров. Ибо правильно initWithDuck:(NSDuck*)duck
                  Во-вторых, бить по пальцам за отсутствие форматирования.
                  Сравните:

                              NSBear *bear = [[NSBear alloc] initWithDuck:[[NSDuck alloc] initWithEgg:[[NSEgg alloc] initWithNeedle:[[NSNeedle alloc] init]]]
                                                                      egg:[[NSEgg alloc] init]];
                  
                              NSBear *bear = [[NSBear alloc] initWithDuck:[[NSDuck alloc] initWithEgg:[[NSEgg alloc] initWithNeedle:[[NSNeedle alloc] init]]
                                                                                            secondEgg:[[NSEgg alloc] init]]];
                  
                  
                              NSBear *bear = [[NSBear alloc] initWithDuck:[[NSDuck alloc] initWithEgg:[[NSEgg alloc] initWithNeedle:[[NSNeedle alloc] init]
                                                                                                                                egg:[[NSEgg alloc] init]]]];
                  

                  В-третьих, стоило бы тут вынести агрументы ав отдельные переменные
                  В-четвёрных, от этого кода пахнет архитектурной… попой.

                  P.S.: форматеры хабра и pastebin'а пытаются выравнять код по 80 символов, что в реалях Obj-C — идиотское занятие. Скопируйте в XCode, и смотрите там.


                  1. Krypt
                    16.12.2013 10:14

                    Кстати, код всё равно кривой. Если нет ARC — будет течь память. Ибо, по конвенции, функции, init* и copy возвращают значение с retainCount = 1. Объексты с retainCount = 0 возвращают функции вида [NSNeedle needle].


                    1. Yan169 Автор
                      16.12.2013 10:14

                      Объекты с «retainCount = 0» никакие методы не возвращают. См. autorelease и NSAutoReleasePool.


                      1. Krypt
                        16.12.2013 10:14

                        Да понятно, что не возвращает. Назовём это «когда-нибудь 0».


                      1. Yan169 Автор
                        16.12.2013 10:14

                        Нет. Если уж Вы решили вспомнить manual reference counting (хотя это абсолютно не к месту), то конвенцию надо приводить полностью и правильно (“alloc”, “new”, “copy”, “mutableCopy”, никакого «init»!), и не писать очевидную чушь. Вас могут читать новички в Objective-C.


                      1. Krypt
                        16.12.2013 10:14

                        Не придерайтесь, это к вопросу читабельности кода не относится.
                        А новичкам — брысь читать эту статью: Advanced Memory Management Programming Guide

                        P.S.: Оригинально вы выражаете несогласие в споре. Думаю, в таком случа, нет смысла его продолжать.


                      1. notxcain
                        16.12.2013 10:14

                        Вам бы самим почитать еще раз. Предыдущий ответ вы написали на автомате, значит не до конца поняли.


                      1. Krypt
                        16.12.2013 10:14

                        Давайте не будем играть в телепатию. Как показала эта ветка — она не работает.


                  1. Yan169 Автор
                    16.12.2013 10:14

                    Ясно, что обсуждаем абстрактный пример в вакууме. И в равных условиях (что есть форматирование, что нет — вариант с меньшим количеством скобок читабельнее).
                    А ваш вариант не по теме, я же условие поставил: классы — универсальные контейнеры, реализующие протокол NSContaining (имена селекторов специально сократил, чтоб не так широко было). Или вы универсальные протоколы не используете, и классы у вас всегда жестко связаны?


                    1. Krypt
                      16.12.2013 10:14

                      Гм, зря сократили. Вводит в заблуждение.
                      В любом случае, последние 3 пункта не отменяются.

                      > Или вы универсальные протоколы не используете, и классы у вас всегда жестко связаны?
                      Практически не использую, кстати. Для хранения данных намного лучше подходят классы с конкретными именами и конкретными типами полей.
                      По поводу универсальных контейнеров у меня для есть отличный комментарий: govnokod.ru/12170#comment161695


                      1. Yan169 Автор
                        16.12.2013 10:14

                        Практически не использую, кстати.

                        Жесткая связанность классов — наше всё?
                        Если строим иерархию, например, сотрудников, то вместо универсального протокола {начальник; подчиненные} сотрудники будут выглядеть так:
                        ДиректорВасильев {ЗаместительКрючков; ЗаместительСвязнов}
                        ЗаместительКрючков {ДиректорВасильев; НачальникОтделаКадровПучков; НачальникОтделаФинансовТранжиров}
                        ...
                        

                        Каждой сущности — свой класс. Очевидно нелепо, но именно так выглядит ваша поправка с Медведем, Уткой и Яйцами. Чтобы Утку заменить на Курицу, придется менять класс Медведя.


            1. kjuby
              16.12.2013 10:14

              вроде бы со скобками все ок, а вот тип переменной death не очень уж совпадает с типом объекта коробки


              1. Yan169 Автор
                16.12.2013 10:14

                В коде синтаксически некорректная конструкция, её невозможно скомпилировать, какие бы интерфейсы классам ни задать. Проверяйте.


        1. CRImier
          16.12.2013 10:14

          умышленная ошибка

          Не niddle, а needle ;-)


  1. Agent_Smith
    16.12.2013 10:14

    Отсутствие скобочек, куча сахара, итераторы, дефолтные значения параметров это круто очень. Но синтаксис просто жесть, все вывернуто через зад, не понятно зачем. Лучше бы они пошли по пути RubyMotion и MonoTouch, где куча сахара и плюшек из оригинальных языков, и при этом все легко читается, и порог входа сильно ниже.


    1. Yan169 Автор
      16.12.2013 10:14

      Objective-C:

      [obj makeBoxWithOrigin: origin andSize: size];
      

      Ruby Motion:
      obj.makeBoxWithOrigin(origin, andSize: size)
      

      Eero:
      obj.makeBoxWithOrigin: origin, andSize: size
      

      По-моему, явных преимуществ у Ruby нет.
      Зато в Eero есть плюсы: это тот же Objective-C со статической типизацией, категориями, расширениями классов и пр. Ruby же изначально не писался под runtime Objective-C, и могут возникать сложности в самых простых местах (пример отсюда):
      Objective-C
      @interface CustomView : UIView
      @property (copy) NSString *text;
      @end
      
      @implementation CustomView
      - (id) initWithFrame:(CGRect)frame {
        [super initWithFrame:frame];
        self.text = @"";
        return self;
      }
      - (id) initWithText:(NSString*)text {
        UIFont *font = [UIFont systemFontOfSize:12];
        CGRect size = [text sizeWithFont:font];
        // skip local initializer
        [super initWithFrame:{{0, 0}, size}];
        self.text = text;
        return self;
      }
      @end
      

      Ruby Motion
      class CustomView < UIView
        alias :'super_initWithFrame:' :'initWithFrame:'
      
        def initWithFrame(frame)
          super.tap do
            @text = ''
          end
        end
      
        def initWithText(text)
          font = UIFont.systemFontOfSize(12)
          size = text.sizeWithFont(font)
          super_initWithFrame([[0, 0], size]).tap do
            @text = text
          end
        end
      end
      

      Eero
      interface CustomView : UIView
        String text {copy}
      end
      
      implementation CustomView
      
        initWithFrame:CGRect, return instancetype = self
          if (self = super.initWithFrame: frame)
            self.text = ''
      
        initWithText:String, return instancetype = self
          font := UIFont.systemFontOfSize: 12
          size := text.sizeWithFont: font
          CGRect rect = {{0, 0}, size}
          if (self = super.initWithFrame:rect)
            self.text = ''
      
      end
      

      Это конечно не отменяет того, что в Ruby Motion много своих плюсов.

      В Mono Touch же вообще нужно биндинг для использования Objective-C кода писать, насколько я знаю.


  1. egormerkushev
    16.12.2013 10:14

    А я обожаю скобки!


  1. cypok
    16.12.2013 10:14

    Про ObjC: не синтаксис в языке главное.


  1. Triang3l
    16.12.2013 10:14

    Чем обосновываете удаление goto?


    1. EndUser
      16.12.2013 10:14

      Andy Arvanitis просто hater — goto это не управляющая команда, а способ мышления.

      Даже убрав эту команду, Andy Arvanitis продолжает ваять if return else return.

      Уверен, он и break оставил.

      Поэтому считаю, что удаление goto не обосновано ни разу, кроме как капризностью.


      1. corristo
        16.12.2013 10:14

        А Вы давно goto видели где-то за пределами чистого Си? В каком-нибудь ядре Linux оно нужно и оправдано, зачем оно в более высокоуровневом языке?


        1. EndUser
          16.12.2013 10:14

          Выход из вложенных циклов же!


        1. Triang3l
          16.12.2013 10:14

          Ну лично я сам использую в C++.

          ...
          	m_pLockedData = new unsigned char[memoryRequired];
          	ComputeVertexDescription(m_pLockedData + m_FirstUnwrittenOffset);
          	desc.m_nFirstVertex = 0;
          	desc.m_nOffset = m_FirstUnwrittenOffset;
          	return true;
          fail:
          	ComputeVertexDescription(NULL, 0, desc);
          	desc.m_nFirstVertex = 0;
          	desc.m_nOffset = 0; 
          	return false;
          


          ...
          	return handle;
          fail_plink:
          	glDeleteProgram(program);
          fail_pcreate:
          	glDeleteShader(pixelShader);
          fail_ps:
          	glDeleteShader(vertexShader);
          fail_init:
          	return SHADER_PROGRAM_HANDLE_INVALID;
          


          1. ilammy
            16.12.2013 10:14

            А можно было сделать на RAII без goto. В простых случаях это несколько длиннее, но зато более понятно и устойчиво. Это что касается освобождения ресурсов и обработки ошибок. Альтернатив goto для вложенных циклов я не вижу (выделить в функцию и сделать return — то же, только в профиль).


  1. deleted-lucky_brick
    16.12.2013 10:14

    фуу.

    так же убого как coffee script

    скобки это круто знает каждый лиспер.

    во что круто =)

    common-lisp.net/projects/cl-objc/cl-objc.pdf


  1. notxcain
    16.12.2013 10:14

    Не нравится. О чем я реально мечтаю так это Scala c синтаксисом Smalltalk.


  1. pravic
    16.12.2013 10:14

    Начали за здравие, а закончили за упокой. Убрали квадратные скобки — круто, но зачем отступы в стиле питон?

    openFile String, [withPermissions: String], return FileHandle

    Плохо читать оторванные друг от друга токены. Лучше бы сделали привычный стиль:

    FileHandle openFile(String, [withPermissions: String])

    handle = FileHandle.fileHandleForReadingAtPath(file)


    1. Yan169 Автор
      16.12.2013 10:14

      Такой синтаксис для Objective-C неоднозначен.

      Представьте следующий интерфейс:

      typedef id(^FileHandleFactoryBlock)(NSString *);
      @interface FileHandle : NSObject
      +(instancetype)fileHandleForReadingAtPath:(NSString *)path;
      +(FileHandleFactoryBlock)fileHandleForReadingAtPath;
      @end
      

      Что произойдёт в результате вызова FileHandle.fileHandleForReadingAtPath(file) ? Будут отослано сообщение fileHandleForReadingAtPath: с одним аргументом или fileHandleForReadingAtPath без аргументов с последующим вызовом полученного блока?

      Я понимаю, что пример надуманный. Но если синтаксис создаётся специально под Objective-C рантайм, он определенно должен быть однозначен.


  1. corristo
    16.12.2013 10:14

    Не смотря на спорный синтаксис, первый убегу программировать на этом языке, если сделают parametrized types


    1. Yan169 Автор
      16.12.2013 10:14

      Я не стал раздувать статью, но в Eero поддерживается instancetype не только для возвращаемых значений, но и для аргументов.
      Это позволяет делать что-то типа типизированных функторов. Упрощенный пример для типизированных коллекций:

      stringArray := (String<MutableArray>)MutableArray.new
      stringArray.addObject: '123'
      stringArray.addObject: @13 // Warning: incompatible pointer type
      
      numArray := (Number<MutableArray>)MutableArray.new
      numArray.addObject: @13
      numArray.addObject: 'ABC'  // Warning: incompatible pointer type
      

      (Класс MutableArray должен быть прокси, перенаправляющий сообщения из интерфейсов NSString/NSNumber всем элементам коллекции)

      Не полноценные parametrized types конечно, но всё же.

      Подробнее про instancetype: nshipster.com/instancetype/
      Про типизированные коллекции в Objective-C: www.jonmsterling.com/posts/2012-02-05-typed-collections-with-self-types-in-objective-c.html