Pull to refresh

Comments 63

Увидев ":=" я сразу вспомнил Pascal и Delphi… Эххх были времена… :)
А теперь ещё и в Go.
Во-первых, в Eero для выделения блоков вместо фигурных скобок используются отступы, как в Python или Ruby.


Это какие-то неправильные руби
И правда, что-то меня переклинило, спасибо. :)
Диспозиция:

@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 — он так тоже должен уметь.
Примерно так:
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
Собственно в этом коже getter не нужен даже, надо только функцию в - (int) test переименовать.

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

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


?

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

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


Хотя иногда, например в лямбде, бывает удобно назвать переменную так же, как и снаружи, ибо другого нормального имени не придумаешь (пишу про CoffeeScript, меня там это пару раз доставало)
Да, иногда это бывает полезно. С другой стороны, защищает от потенциальных ошибок. Чисто субъективно, я бы предпочел вариант с warning, а не с compile error.
У меня как раз наоборот — ошибки лезут когда приходится создавать новую переменную, а обращаюсь случайно к старой.
Правда это специфично для Haskell. Там нельзя написать
let n = n+1 in ...
надо давать новое имя
let n1 = n+1 in ...

В ML можно, в Haskell придумал замену
case n+1 in n -> ...
но уж больно многословно получается.
Нет, в вашем коде используется одна и та же переменная, так естественно можно писать. Запрещено в локальной области видимости определять новую переменную с тем же именем, что и в родительской. В Objective-С/C так делать можно (можно настроить, чтобы выдавался warning).
BOOL status = NO;
if (...){
    BOOL status = YES;
}
Забавно, как побаловаться.
Но пока шило на мыло помоему.
В любом случае многие очень сильные проекты начинались именно вот с такого вот рода экспериментов.
Какая-то хипстерота. Читать невозможно. Функции как списки переменных выглядят.
Вообще непонятно зачем? У всех языков свой синтаксис, странным он кажется только тем людям, которые его просто видят в первый раз, если человеку необходим этот язык он начнет его учить и через уже месяц не будет никаких проблем, а, возможно, он даже ему понравится намного больше чем остальные, тут есть множество своих плюсов, как и в любом другом языке. Давайте для Java создадим тоже свой Ero с блекджеком и щлюхами, я подозреваю, что для людей, программирующих на ассемблере или Pascale Java тоже кажется с очень странным синтаксисом.
На вопрос «зачем?» автор отвечает: «для читаемости, т.к. код читается гораздо чаще, чем пишется».
Вообще, я согласен, что привыкаешь ко всему, Objective-C лично я воспринимаю абсолютно нормально, особенно после 5 лет разработки. :)
Но объективно говоря, код на Eero получается куда лаконичнее, плюс дополнительные функции, такие как опциональные аргументы, перегрузка операторов, полная поддержка instancetype, итераторы типа for int i in 0 .. 10 и прочее. Т.е. дело не только в синтаксисе.
Ну если бы не другой синтаксис — возможно, я бы его даже попробовал. А так… Нет, я останусь на Obj-C :)
Мне Objective-C нравится: философией, рантаймом. Но от синтаксиса я не в восторге, хотя и привык. По-моему, если подходить непредвзято, и забыть про привычки, то можно объективно сказать, что синтаксис Objective-C далек от совершенства: для тех же самых функций можно было придумать куда более удобную запись.
Код Obj-C, раза в 1.5-2 длиннее по сравнению с тем же C#, это да. Но по сравнению с C++ я выигрыша не вижу. Причём если в Obj-C однозначно, то в случае с С++ — кто как хочет, тот так и городит. Так что я бы на синтаксис Obj-C не особо жаловался :)
И у этого языка свой синтаксис, при обеспечении совместимости по библиотекам. Что плохого то?
Нам не нравится непонятный синтаксис обжСи, поэтому мы сделали свой непонятный синтаксис! Теперь непонятных синтаксисов два.

При всем моём неприятии синтаксиса обж си,
name,   return String = 'Macintosh'
format, return String = 'HFS+'

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

А Python-стиль для C — это как-то неправильно совсем запутывает, имхо.
В C++ нет именованных параметров, поэтому его синтаксис никак не применить.
Это даже не именованные параметры, а имя метода состоящее из нескольких слов перемежающихся параметрами.
Прелесть именованных параметров в том, что их можно писать в произвольном порядке.
Так сигнатуры методов это же божественная часть Objective C! Не понимаю людей которым ObjC не нравится.
Хотя мое мнение никого не интересует, но скажу
Мне очень нравятся квадратные скобки

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

Никто не поправил очевидно потому, что в скобочках таки легко запутаться. :)
Зачем замарачиваться скобками, если их компилятор проверит ;)
Чтобы не было ситуации «чукча не читатель, чукча писатель». Код читается намного чаще, чем пишется.

Кроме того, компилятор не всесилен. Предположим, мы решили понизить связанность классов и сделать их более универсальными, так, чтобы в одной сущности могло находиться по 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!
За такой код руки отрывать надо.
Во-первых, можно сразу бить в табло за кривые имена параметров. Ибо правильно 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, и смотрите там.
Кстати, код всё равно кривой. Если нет ARC — будет течь память. Ибо, по конвенции, функции, init* и copy возвращают значение с retainCount = 1. Объексты с retainCount = 0 возвращают функции вида [NSNeedle needle].
Объекты с «retainCount = 0» никакие методы не возвращают. См. autorelease и NSAutoReleasePool.
Да понятно, что не возвращает. Назовём это «когда-нибудь 0».
Нет. Если уж Вы решили вспомнить manual reference counting (хотя это абсолютно не к месту), то конвенцию надо приводить полностью и правильно (“alloc”, “new”, “copy”, “mutableCopy”, никакого «init»!), и не писать очевидную чушь. Вас могут читать новички в Objective-C.
Не придерайтесь, это к вопросу читабельности кода не относится.
А новичкам — брысь читать эту статью: Advanced Memory Management Programming Guide

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

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

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

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

Не niddle, а needle ;-)
Отсутствие скобочек, куча сахара, итераторы, дефолтные значения параметров это круто очень. Но синтаксис просто жесть, все вывернуто через зад, не понятно зачем. Лучше бы они пошли по пути RubyMotion и MonoTouch, где куча сахара и плюшек из оригинальных языков, и при этом все легко читается, и порог входа сильно ниже.
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 кода писать, насколько я знаю.
Про ObjC: не синтаксис в языке главное.
Andy Arvanitis просто hater — goto это не управляющая команда, а способ мышления.

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

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

Поэтому считаю, что удаление goto не обосновано ни разу, кроме как капризностью.
А Вы давно goto видели где-то за пределами чистого Си? В каком-нибудь ядре Linux оно нужно и оправдано, зачем оно в более высокоуровневом языке?
Выход из вложенных циклов же!
Ну лично я сам использую в 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;
А можно было сделать на RAII без goto. В простых случаях это несколько длиннее, но зато более понятно и устойчиво. Это что касается освобождения ресурсов и обработки ошибок. Альтернатив goto для вложенных циклов я не вижу (выделить в функцию и сделать return — то же, только в профиль).
Не нравится. О чем я реально мечтаю так это Scala c синтаксисом Smalltalk.
Начали за здравие, а закончили за упокой. Убрали квадратные скобки — круто, но зачем отступы в стиле питон?

openFile String, [withPermissions: String], return FileHandle

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

FileHandle openFile(String, [withPermissions: String])

handle = FileHandle.fileHandleForReadingAtPath(file)
Такой синтаксис для Objective-C неоднозначен.

Представьте следующий интерфейс:
typedef id(^FileHandleFactoryBlock)(NSString *);
@interface FileHandle : NSObject
+(instancetype)fileHandleForReadingAtPath:(NSString *)path;
+(FileHandleFactoryBlock)fileHandleForReadingAtPath;
@end

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

Я понимаю, что пример надуманный. Но если синтаксис создаётся специально под Objective-C рантайм, он определенно должен быть однозначен.
Не смотря на спорный синтаксис, первый убегу программировать на этом языке, если сделают parametrized types
Я не стал раздувать статью, но в 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
Sign up to leave a comment.

Articles