Здравствуйте, adontz, Вы писали: A>Здравствуйте, VladD2, Вы писали: Q>>>Топикстартер предоставил синтаксис определения, а не вызова. Вызов, я так полагаю, по прежнему будет new MyClass(params). VD>>"new", кстати, тоже лишнее
Is this invocation of Method or construction of object?
A>
A>public class MyClass
A>{
A>}
A>public class YourClass
A>{
A> private static MyClass MyClass()
A> {
A> return null;
A> }
A> public YourClass()
A> {
A> // Syntax without 'new' keyword.
A> // Is this invocation of Method or construction of object?
A> MyClass x = MyClass();
A> }
A>}
A>
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, VladD2, Вы писали:
Q>>>Топикстартер предоставил синтаксис определения, а не вызова. Вызов, я так полагаю, по прежнему будет new MyClass(params). VD>>"new", кстати, тоже лишнее
A>
A>public class MyClass
A>{
A>}
A>public class YourClass
A>{
A> private static MyClass MyClass()
A> {
A> return null;
A> }
A> public YourClass()
A> {
A> // Syntax without 'new' keyword.
A> // Is this invocation of Method or construction of object?
A> MyClass x = MyClass();
A> }
A>}
A>
Если считать что конструктор — это функция, возвращающая объект, то подобный код просто не будет компилироваться: компилятор расскажет о неоднозначности вызова MyClass().
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Способ именования конструкторов и деструкторов
Здравствуйте, maykie, Вы писали:
M>Третий. Первый противоречит DRY, второй из-за того что если в языке всё передаётся по ссылке и разрешена перегрузка операторов, то будет неоднозначность с operator():
Ну такая неоднозначность легко разрешима, например в D вызов this(x) это всегда вызов конструктора, operator()
(который в D имеет вид result_type opCall(params) ) внутри класса можно вызывать только явно this.opCall(x);
A>public class MyClass
A>{
A>}
A>public class YourClass
A>{
A> private static MyClass MyClass()
A> {
A> return null;
A> }
A> public YourClass()
A> {
A> // Syntax without 'new' keyword.
A> // Is this invocation of Method or construction of object?
A> MyClass x = MyClass();
A> }
A>}
A>
Здравствуйте, maxkar, Вы очень много понаписали:
M>Все нижесказанное возникло в результате размышления над тем, что мне не удобно писать... В классических языках ООП решает слишком много задач... При такой автоматической конвертации получается проблема — теряется identity объекта... Что такое интерфейс в общем случае?.. Можно автоматически генерировать "accessor methods" для интерфейса... Можно и сахар для одновременного объявления типа и конвертора сделать... Можно сделать и отдельные экземпляры с двумя base... Для доступа к «защищенным» полям передавать соответствующие «интерфейсы»... Ну и последнее... Очень много их них заменяется на instance creation method с замыканиями...
Ничего не понял, но от греха подальше плюсанул. Твой комментарий тронул меня до глубины души.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Способ именования конструкторов и деструкторов
Здравствуйте, maxkar, Вы писали:
M>Первая же претензия. Не указано, какую задачу решает конструктор. В mainstream (c++/java/c#) определение конструктора на самом деле определяет две сущности: M> M> Instance initialization method — инициализация экземпляра. Выполняет следующие действия: M> Instance creation method — создание экземпляра. Выполняет следующие действия: M>
initialization. Creation лично у меня обычно ассоциируется со статическим оператором new (если нужно как-то по-особому выделить память для класса).
XC>>Каждый метод предваряется некоторым ключевым словом: (skip) M>A!!! :maniac: Блин, они гвоздями к объекту прибиты? Ну неудобно это. И я против сегрегации методов. Возьмем тот же прямоугольник. Не, мало. Массив прямоугольников! rectangles. И отконвертируем их в массив высот этих прямоугольников. Напишем, Array.map(rectangles, ???). И что туда писать? Анонимную функцию? Или Rectangle.getHeight? Кстати, кто такой тогда будет Rectangle.getHeight? M>Зато если написать внешнюю функцию RectangleUtils.getWidth(rectangle : Rectangle) : int, ее можно туда передать.
Если в языке разрешить использовать нестатические методы класса как статические с дополнительным аргументом this, это поможет?
class MyClass
{
public int Func();
};
MyClass obj;
obj.Func();
MyClass.Func(obj); // вот так тоже можно
Это что-то вроде "обратных методов-расширений". Тогда любые методы будут "принадлежать типу", если это нужно.
Re[4]: Способ именования конструкторов и деструкторов
Здравствуйте, x-code, Вы писали:
XC>Если в языке разрешить использовать нестатические методы класса как статические с дополнительным аргументом this, это поможет?
Да. И очень сильно. Ту проблему решит. Правда нужно будет решить, можно ли все методы, работающие с типом, обозвать его extensionMehtod. В идеале хотелось бы полную симметричность. Т.е. метод объекта может быть и "обратным extension-method", и любой метод, работающий с объектом (в качестве первого аргумента), может считаться его extension method. На примере — написан какой-то метод, изменяющий сложную структуру (тот же UI для толстого клиента, где живет состояние). С сигнатурой doSomethingWith(object). И хочется конкретный вызов метода с конкретным объектом передать в другой вызов (например, это будет обработчик нажатия на кнопку). Т.е. в чистом виде нужно
вместо
... object = ...
const callback : Function = function() : void {
doSomethingWithObject(object);
}
add(new Button(new Action(callback)));
удобно было бы
... object = ...
add(new Button(object.doSomethingWithObject));
Проблема стоит только для методов с одним аргументом. Для методов с двумя и более аргументами все прекрасно, так как работает currying и функция не вычисляется. А вот для одного аргумента так не получится. Можно отдельную функцию написать, которая такую оболочку делает, в принципе не проблема. Но здесь вопрос об оправданности разделения extension method и "просто метод, работающий с типом". Подробно этот вопрос не исследовал, может быть, возникнет противоречие с чем-нибудь еще. Возможно, будут проблемы с определением, какой же именно метод имелся в виду, нужно смотреть, позволяют ли такое extension method. Вообще, если организация функций помодульная (а не пообъектная) для разрешения неоднозначностей может быть что-то вроде
object.[some.module.Name:doSomethingWithObject]
С организацией по объектам/по классам такое не очень красиво смотрится.
Re[6]: Способ именования конструкторов и деструкторов
Здравствуйте, FR, Вы писали:
FR>Здравствуйте, maxkar, Вы писали:
FR>Интересно, но по моему слишком сложно и хрупко, и не вижу чем лучше той же простой по сути структурной типизации для объектов (OCaml).
Замечание принято. Что хрупко — естественно, в стройную и надежную систему я пока собирать не пытался. Т.е. не определял, что нужно сделать синтаксическим сахаром, что — не нужно а что друг-другу противоречит. Это больше набор хотелок и возможных вариантов их получения. Поэтому же и сложно получается. А вышеизложенные соображения — это попытка ответить на вопрос "зачем вообще выделять объектный тип среди других типов и что нужно добавить к абстрактным типам данных, чтобы получить то же, что дает ООП".
Здесь сыграла важную роль книга "Practical API Design: Confessions of a Java Framework Architect". Там сформулирована мысль о том, что предоставлять функциональность библиотеки следует классами, а вот то, что библиотеке требуется от внешнего мира — интерфейсами. Обоснование — возможность добавлять новые методы в интерфейс, не ломая клиентского кода (который может тоже реализовать интерфейс, предоставляемый библиотекой). Попробовал на практике следовать указанной рекомендации — проблем не составляет. Только вот возникакет вопрос — а зачем тогда вообще нужно отдельное понятие интерфейса? Ведь, по сути, интерфейс в этом случае — простой record, который передается в библиотеку. Еще одним аргументом за record было наличине в интерфейсах методов вроде getSomething(), где это something не меняется во время работы с объектом. Например, какой-нибудь getCapacity() в библиотеке, которая не предполагает, что вместимость может изменяться со временем. В record'е неизменяемое значение смотрелось бы естественнее (в данном случае интерфейс — это именно то, что требует а не предоставляет библиотека). Можно даже и без record, но для количества в 3 и более методов все же лучше их называть.
Далее, практическое наблюдение. У меня по возможности проект собирается на основе множества небольших библиотек, занимающихся определенными "незначительными" аспектами. Можно их при сборке и в одну собрать, это не принципиально. Принципиально то, что на самом деле в этих библиотеках получаются не "классы" а "типы данных". Наследованием там вообще не пахнет (ну не нужно оно, а все что нужно для изменения поведения передается явно), а без этого разница между объектами и "просто типами" в синтаксисе вызова методов ну и возможности приведения к "интерфейсам". Ситуация усугубляется еще и тем, что у меня в языке (ActionScript 3.0 ) нет перегрузки конструкторов. Поэтому если возможностей создания объекта несколько, приходится писать instance creation methods. Ну а так как все instance creation methods равноправны, конструтор можно было бы не делать публичным. Методы объекта по сути — те же методы работы с abstract type. Ну разница в синтаксисе, я уже показал в другом сообщении, можно два варианта сделать эквивалентными (через точку и через метод, получающий this (обратный метод-расширение, как их назвал x-code)). На модульную структуру для организации функций все как раз гораздо удобнее ложится, чем попытки вспомнить, в какой же хелпер был положен extension method.
Ну и еще одно наблюдение. На небольшом проекте (что-то около 120 тысяч строк) при используемой организации (пишем удобные библиотеки, а не framework'и, навязывающие схему написания + интерфейсы — для входных данных в библиотеки, на выходе — классы, может быть — без публичных конструкторов) я вообще не вижу никаких преимуществ в классическом "ООП". Ну да, наследование используется в паре мест. Это из-за отсутствия алгебраических типов (type x = VariantA | VariantB | ...) и нормальных record (там как раз обработчики с частичной заменой, уже упоминал). На них вместо наследования с переопределением методов был бы someRecord with {...}. В остальных местах при необходимости композиция (для поддержки событий, UI все же, без него — никак). Никаких неудобств не испытываю. Вот и возник вопрос — а что еще есть в ООП такого, что делает его удобным для кодирования? Может, я чего-то не понимаю Ну вот из того, что нашел — это is-a отношение, которое было не нужно. Ну можно задать его извне. Не помню, можно ли так делать в haskell, или все же нужно при описании типа указывать. Все остальные практические задачи делаются прямолинейно. Нужно что-то переопределять в поведении — передаем метод, который будет вести себя по-разному. Нужно переиспользовать функциональность — используем композицию. Кстати, у себя вроде бы не встречал, что при композиции выставляется весь интерфейс "родительского" объекта. Для событий выставляются только интерфейсы подписки/удаления. Для UI-компонентов выставляется визуальная часть, getUI(). Класс же, по сути, обеспечивает взаимодействие между моделькой и UI. Можно было вообще на замыканиях делать, но просто кода много было бы внутри замыкания, не удобно. Проще выделить новый "тип".
Что касается структурной типизации. Ага, вполне устроило бы. Но только опять же, не понятно, зачем мне там классы... Почему бы не ввести структурную типизацию для record'ов или их подмножества (немодифицируемые). Все те 120 тысяч строк я бы сделал на OCaml'е на абстрактных типах данных (и вообще типах, локальных для модуля, где это можно). По сути они именно так и написаны, только в тексте изложены на языке объектной модели, которую приходится использовать. Не вижу пока никаких преимуществ, которые дали бы "классы". Кстати, что интересно, в объектах ocaml'а ведь нет неявного отношения child is-a parent, его писать нужно явно. А то, куда передаются интерфейсы, по сути использует именно хитрый вариант типизации "нужны методы A, B, C". Вот их и можно попытаться найти в некоторых случаях из текущего контекста (по именам и т.п.), зачем для этого вводить еще один метатип "объект"? Список методов в указанном контексте известен, можно выбирать (объект, для которого выбирать и строить можно и указать явно в какой-то конструкции). Чего действительно не хватало бы:
Возможности получать для одноаргументных методов "хитрое" замыкание. Уже показывал где-то, передать функцию, работающую над заданным объектом, а не результат выполнения этой функции. Пусть даже через value.method и вызов этого метода value.method () вместо method value. Делается вручную, но так как часто нужно — не очень красиво.
Возможность разрешения функций по первому типу аргумента. Т.е., например при импорте open List; open Hashtbl при вызове fold smth ... выбирался бы fold, применимый к объекту (в зависимости от того, smth является списком или таблицей — List.fold или Hashtbl.fold). Пусть даже для этого опять нужно было бы использовать синтаксис smth.fold .... Перегрузки по другим типам (другим аргументам функции) не нужно. И вообще это даже не введение "перегруженных" методов, это просто механизм lookup для имени (из всех доступных выбирается доступное по типу первого аргумента). Это позволило бы не писать кучи лишних квалификаторов модулей (то, что List.fold нет я знаю, это для примера). Хотя вот не знаю, как такое "усовершенствование" повлияет на вывод типов.
Вот. Надеюсь в таком контектсте станет понятнее, откуда вообще пошли те идеи. Я не пытался изобразить что-то особо крутое, я просто примерял ООП и "просто типы" к решению практических задач. Ну и анализировал, где и на что больше всего похож код, загнанный в рамки платформы и чего хотелось бы от этой самой платформы.
Здравствуйте, maxkar, Вы опять много понаписали:
M>Замечание принято. Что хрупко — естественно, в стройную и надежную систему я пока собирать не пытался... Здесь сыграла важную роль книга "Practical API Design: Confessions of a Java Framework Architect"... Далее, практическое наблюдение... Ну и еще одно наблюдение... Что касается структурной типизации... Вот. Надеюсь в таком контектсте станет понятнее, откуда вообще пошли те идеи...
Ты не мог бы разбивать свои комментарии на несколько частей? Так будет проще воспринимать, улавливать основную идею, выделять независимые ветки обсуждения, независимо оценивать. Ещё было бы удобно, если бы комментарии содержательно озаглавливались, так их проще искать в ветке обсуждения.
Здравствуйте, Qbit86, Вы писали:
Q>Ты не мог бы разбивать свои комментарии на несколько частей? Так будет проще воспринимать, улавливать основную идею, выделять независимые ветки обсуждения, независимо оценивать. Ещё было бы удобно, если бы комментарии содержательно озаглавливались, так их проще искать в ветке обсуждения.
Присоединяюсь к просьбе. Понятно, что когда идеи живут в голове автора, то все просто и понятно, но ИМХО при изложении многое потерялось... Остается впечатление — что-то интересное, но вот что именно?:)
По крайней мере нестандартные синтаксические конструкции хорошо бы дополнить комментариями — что есть что.
Здравствуйте, Qbit86, Вы писали:
M>>Все нижесказанное возникло в результате размышления над тем, что мне не удобно писать... В классических языках ООП решает слишком много задач... При такой автоматической конвертации получается проблема — теряется identity объекта... Что такое интерфейс в общем случае?.. Можно автоматически генерировать "accessor methods" для интерфейса... Можно и сахар для одновременного объявления типа и конвертора сделать... Можно сделать и отдельные экземпляры с двумя base... Для доступа к «защищенным» полям передавать соответствующие «интерфейсы»... Ну и последнее... Очень много их них заменяется на instance creation method с замыканиями...
Q>Ничего не понял, но от греха подальше плюсанул. Твой комментарий тронул меня до глубины души.
Вы все еще читаете посты с шизофренической пунктуацией?
Re[2]: Способ именования конструкторов и деструкторов
On 11/04/11 18:03, ononim wrote:
> __>И все делается само > а если хочется два конструктора с разными параметрами?
Мало ли что хочется, может лучше не надо?..Интересно, но в Скале это почти так. Красиво, имхо.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: Способ именования конструкторов и деструкторов
Здравствуйте, maxkar, Вы писали:
M>Проблема стоит только для методов с одним аргументом. Для методов с двумя и более аргументами все прекрасно, так как работает currying и функция не вычисляется. А вот для одного аргумента так не получится. Можно отдельную функцию написать, которая такую оболочку делает, в принципе не проблема.
Когда я анализировал эту проблему, пришел к выводу, что для передачи функций как аргументов корректнее использовать синтаксис, отличный от синтаксиса вызова функции. Для формирования функциональных объектов с частично сформированными аргументами вместо круглых скобок используются фигурные. Это кстати вписывается в интуитивно понятную схему — фигурные формируют данные (массивы, структуры), круглые — вычисляют (порядок действий, аргументы функций).
Например (я применяю наиболее оптимальный синтаксис для типов функциональных объектов "int=>int", "(char,float)=>void" и т.п.)
int Foo1(int x, int y);
int Bar1(int=>int f);
int Foo2(int z);
int Bar2(void=>int f);
Bar1(Foo1{100}); // x = 100, y остается аргументом
Bar1(Foo1{_,200}); // y = 200, x остается аргументом
Bar1(Foo1{.y=300});// y = 300, x остается аргументом
Bar2(Foo2{300});
Впрочем, этот вопрос уже слишком далеко отошел от конструкторов/деструкторов, по которым у меня действительно были сомнения:) Благодаря rsdn кстати я и пришел к своему решению. Если будет интерес, буду создавать время от времени такие темы с обсуждением различных синтаксических решений, а вообще я надеюсь что скоро открою сайт со всеми этими материалами.
Re: Способ именования конструкторов и деструкторов
Здравствуйте, x-code, Вы писали:
XC>Как думаете, какой способ предпочтительнее с точки зрения дизайна языка программирования?
Тут надо сначала разобраться с тем, что означает слово "конструктор". В языках типа C# и Delphi, "конструктор" это нечто совершенно ненужное, грубо говоря первая функция которая педалит объект, выделяемый в динамической памяти (и только там). Как её назвать -- да как угодно. Хоть Create/Destroy, разницы-то.
В языке C++ конструктор это очень мощная абстракция, он может запускаться и отрабатывать в неопределённые моменты времени, поскольку объект может быть определён на стеке, может быть нелокальным статическим, может быть каким угодно ещё. То есть по сути там программист никогда *не вызывает* конструктор и деструктор сам кроме тех редких случаев когда создаёт объект динамически. Программист их *объявлет*, вызовом же занимается компилятор. И здесь лучше не портить жизнь конвенциальностью -- имя конструктора пусть совпадает с именем объекта, имя деструктора с ним же, но с хвостиком. К тому же это сохраняет естественный синтаксис
Объявление пишется так же, как и вызов в первом случае. Во втором -- упс и опаньки.
Но (и это делает ваш вопрос совсем некорректным) есть куча языков где инскрипция "конструктор" понимается ВООБЩЕ офигеть не так. В языке Smalltalk, например, классы это first-class objects