Re[5]: Способ именования конструкторов и деструкторов
От: FR  
Дата: 10.04.11 06:19
Оценка: 1 (1)
Здравствуйте, maxkar, Вы писали:

Интересно, но по моему слишком сложно и хрупко, и не вижу чем лучше той же простой по сути структурной типизации для объектов (OCaml).
Re[5]: Определение, вызов
От: _nn_ www.nemerleweb.com
Дата: 10.04.11 07:48
Оценка:
Здравствуйте, 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>


В Python нет неоднозначности
class MyClass:
    def __init__(self):
        print "MyClass.__init__"

class YourClass:
    @staticmethod
    def MyClass():
        print "YourClass.MyClass"
        return None

    def __init__(self):
        x1 = MyClass()
        x2 = YourClass.MyClass()

y = YourClass()

MyClass.__init__
YourClass.MyClass

http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: Определение, вызов
От: hardcase Пират http://nemerle.org
Дата: 10.04.11 08:54
Оценка:
Здравствуйте, 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]: Способ именования конструкторов и деструкторов
От: FR  
Дата: 10.04.11 10:34
Оценка:
Здравствуйте, maykie, Вы писали:

M>Третий. Первый противоречит DRY, второй из-за того что если в языке всё передаётся по ссылке и разрешена перегрузка операторов, то будет неоднозначность с operator():


Ну такая неоднозначность легко разрешима, например в D вызов this(x) это всегда вызов конструктора, operator()
(который в D имеет вид result_type opCall(params) ) внутри класса можно вызывать только явно this.opCall(x);
Re[5]: Определение, вызов
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.04.11 15:43
Оценка: +1
Здравствуйте, adontz, Вы писали:

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>


Пришла пора поставить Nemerle.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Способ именования конструкторов и деструкторов
От: ononim  
Дата: 10.04.11 15:54
Оценка: :)
4.
class MyClass
{
 ();
 ~();
};


Как много веселых ребят, и все делают велосипед...
Re[5]: Выговорился
От: Qbit86 Кипр
Дата: 10.04.11 18:20
Оценка:
Здравствуйте, maxkar, Вы очень много понаписали:

M>Все нижесказанное возникло в результате размышления над тем, что мне не удобно писать... В классических языках ООП решает слишком много задач... При такой автоматической конвертации получается проблема — теряется identity объекта... Что такое интерфейс в общем случае?.. Можно автоматически генерировать "accessor methods" для интерфейса... Можно и сахар для одновременного объявления типа и конвертора сделать... Можно сделать и отдельные экземпляры с двумя base... Для доступа к «защищенным» полям передавать соответствующие «интерфейсы»... Ну и последнее... Очень много их них заменяется на instance creation method с замыканиями...


Ничего не понял, но от греха подальше плюсанул. Твой комментарий тронул меня до глубины души.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Способ именования конструкторов и деструкторов
От: x-code  
Дата: 11.04.11 07:49
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Первая же претензия. Не указано, какую задачу решает конструктор. В mainstream (c++/java/c#) определение конструктора на самом деле определяет две сущности:

M>

    M>
  1. Instance initialization method — инициализация экземпляра. Выполняет следующие действия:
    M>
  2. 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]: Способ именования конструкторов и деструкторов
От: maxkar  
Дата: 11.04.11 08:24
Оценка:
Здравствуйте, 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]: Способ именования конструкторов и деструкторов
От: maxkar  
Дата: 11.04.11 09:32
Оценка: 21 (2)
Здравствуйте, 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". Вот их и можно попытаться найти в некоторых случаях из текущего контекста (по именам и т.п.), зачем для этого вводить еще один метатип "объект"? Список методов в указанном контексте известен, можно выбирать (объект, для которого выбирать и строить можно и указать явно в какой-то конструкции). Чего действительно не хватало бы:

Вот. Надеюсь в таком контектсте станет понятнее, откуда вообще пошли те идеи. Я не пытался изобразить что-то особо крутое, я просто примерял ООП и "просто типы" к решению практических задач. Ну и анализировал, где и на что больше всего похож код, загнанный в рамки платформы и чего хотелось бы от этой самой платформы.
Re[7]: Разбиение комментариев
От: Qbit86 Кипр
Дата: 11.04.11 09:53
Оценка:
Здравствуйте, maxkar, Вы опять много понаписали:

M>Замечание принято. Что хрупко — естественно, в стройную и надежную систему я пока собирать не пытался... Здесь сыграла важную роль книга "Practical API Design: Confessions of a Java Framework Architect"... Далее, практическое наблюдение... Ну и еще одно наблюдение... Что касается структурной типизации... Вот. Надеюсь в таком контектсте станет понятнее, откуда вообще пошли те идеи...


Ты не мог бы разбивать свои комментарии на несколько частей? Так будет проще воспринимать, улавливать основную идею, выделять независимые ветки обсуждения, независимо оценивать. Ещё было бы удобно, если бы комментарии содержательно озаглавливались, так их проще искать в ветке обсуждения.
Глаза у меня добрые, но рубашка — смирительная!
Re[8]: Разбиение комментариев
От: x-code  
Дата: 11.04.11 10:34
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Ты не мог бы разбивать свои комментарии на несколько частей? Так будет проще воспринимать, улавливать основную идею, выделять независимые ветки обсуждения, независимо оценивать. Ещё было бы удобно, если бы комментарии содержательно озаглавливались, так их проще искать в ветке обсуждения.


Присоединяюсь к просьбе. Понятно, что когда идеи живут в голове автора, то все просто и понятно, но ИМХО при изложении многое потерялось... Остается впечатление — что-то интересное, но вот что именно?:)
По крайней мере нестандартные синтаксические конструкции хорошо бы дополнить комментариями — что есть что.
Re[6]: Выговорился
От: Кодёнок  
Дата: 11.04.11 11:27
Оценка:
Здравствуйте, Qbit86, Вы писали:

M>>Все нижесказанное возникло в результате размышления над тем, что мне не удобно писать... В классических языках ООП решает слишком много задач... При такой автоматической конвертации получается проблема — теряется identity объекта... Что такое интерфейс в общем случае?.. Можно автоматически генерировать "accessor methods" для интерфейса... Можно и сахар для одновременного объявления типа и конвертора сделать... Можно сделать и отдельные экземпляры с двумя base... Для доступа к «защищенным» полям передавать соответствующие «интерфейсы»... Ну и последнее... Очень много их них заменяется на instance creation method с замыканиями...


Q>Ничего не понял, но от греха подальше плюсанул. Твой комментарий тронул меня до глубины души.


Вы все еще читаете посты с шизофренической пунктуацией?
Re[2]: Способ именования конструкторов и деструкторов
От: _nn_ www.nemerleweb.com
Дата: 11.04.11 15:18
Оценка:
Здравствуйте, ononim, Вы писали:

O>
O>4.
O>class MyClass
O>{
O> ();
O> ~();
O>};
O>


O>


А может проще:
class Stock(val name:String, val symbol:String, var price:Double, var change:Double){
}


class MyClass() {}


И все делается само
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Способ именования конструкторов и деструкторов
От: ononim  
Дата: 11.04.11 17:03
Оценка:
__>И все делается само
а если хочется два конструктора с разными параметрами?
Как много веселых ребят, и все делают велосипед...
Re[4]: Способ именования конструкторов и деструкторов
От: . Великобритания  
Дата: 11.04.11 19:16
Оценка:
On 11/04/11 18:03, ononim wrote:

> __>И все делается само

> а если хочется два конструктора с разными параметрами?
Мало ли что хочется, может лучше не надо?..Интересно, но в Скале это почти так. Красиво, имхо.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: Способ именования конструкторов и деструкторов
От: x-code  
Дата: 12.04.11 09:35
Оценка: 5 (1)
Здравствуйте, 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: Способ именования конструкторов и деструкторов
От: Tilir Россия http://tilir.livejournal.com
Дата: 13.04.11 06:28
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Как думаете, какой способ предпочтительнее с точки зрения дизайна языка программирования?


Тут надо сначала разобраться с тем, что означает слово "конструктор". В языках типа C# и Delphi, "конструктор" это нечто совершенно ненужное, грубо говоря первая функция которая педалит объект, выделяемый в динамической памяти (и только там). Как её назвать -- да как угодно. Хоть Create/Destroy, разницы-то.

В языке C++ конструктор это очень мощная абстракция, он может запускаться и отрабатывать в неопределённые моменты времени, поскольку объект может быть определён на стеке, может быть нелокальным статическим, может быть каким угодно ещё. То есть по сути там программист никогда *не вызывает* конструктор и деструктор сам кроме тех редких случаев когда создаёт объект динамически. Программист их *объявлет*, вызовом же занимается компилятор. И здесь лучше не портить жизнь конвенциальностью -- имя конструктора пусть совпадает с именем объекта, имя деструктора с ним же, но с хвостиком. К тому же это сохраняет естественный синтаксис

A(const A&); // объявили
...
A a(a1); // попросили компилятор вызвать


сравните с:

CopyConstructor(const A&); // объявили
...
A a(a1); // попросили.. хмм... ЧТО?



Объявление пишется так же, как и вызов в первом случае. Во втором -- упс и опаньки.

Но (и это делает ваш вопрос совсем некорректным) есть куча языков где инскрипция "конструктор" понимается ВООБЩЕ офигеть не так. В языке Smalltalk, например, классы это first-class objects

Object subclass: #Person 
    instanceVariableNames: 'firstName lastName' 
    category: 'OnSmalltalk'


и создаваться эта радость будет примерно так:

person := (Person new)
                  firstName: 'Ramon'; 
                  lastName: 'Leon'; 
                  yourself.


где new это просто семантическая стрелка "поместите в".

Но мы можем переопределить конструктор:

Person class>>firstName: aFirstName lastName: aLastName
    ^(self new)
        firstName: aFirstName ;
        lastName: aLastName;
        yourself.


и в этом случае создавать уже вот так:

person := Person firstName: 'Ramon' lastName: 'Leon'.


Как вам такой "синтаксис для конструктора"?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.