Re[7]: Определение, вызов
От: Qbit86 Кипр
Дата: 07.04.11 13:19
Оценка:
Здравствуйте, blackhearted, Вы писали:

Q>>Чтобы избежать избыточности (и всех её негативных последствий) в определении класса.

B>и какие негативные последствия у

B>
B>public class A
B>{
B>   public A();
B>}
B>


B>по сравнению с


B>
B>public class A
B>{
B>   public ctor();
B>}
B>

B>? :???:

Примерно те же, что и у отсутствия нормальной формы в базе данных :) Например, аномалия обновления — изменение имени класса в его определении приведёт к ошибке компиляции класса, так как имена конструкторов станут рассогласованы.
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: Определение, вызов
От: jazzer Россия Skype: enerjazzer
Дата: 07.04.11 13:21
Оценка: :))) :)))
Здравствуйте, blackhearted, Вы писали:

B>и какие негативные последствия у


B>
B>public class A
B>{
B>   public A();
B>}
B>


B>по сравнению с



B>
B>public class A
B>{
B>   public ctor();
B>}
B>


B>?


Проще имя класса поменять.
Актуально в редакторах без Search/Replace (например, mspaint).
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[8]: Определение, вызов
От: blackhearted Украина  
Дата: 07.04.11 13:32
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, blackhearted, Вы писали:


B>>и какие негативные последствия у


B>>
B>>public class A
B>>{
B>>   public A();
B>>}
B>>


B>>по сравнению с



B>>
B>>public class A
B>>{
B>>   public ctor();
B>>}
B>>


B>>?


J>Проще имя класса поменять.

J>Актуально в редакторах без Search/Replace (например, mspaint).

исходниик в пейнте редактировать?
Браво.
ту же все рассказывают, что IDE всё подсветит, покажет.
Re[9]: Определение, вызов
От: Anpek  
Дата: 07.04.11 13:34
Оценка: +1 :)
Здравствуйте, blackhearted, Вы писали:

B>исходниик в пейнте редактировать?

B>Браво.
B>ту же все рассказывают, что IDE всё подсветит, покажет.

Отредактировать исходник в пейнте — фигня вопрос. Покажите мне лучше компилятор, который на вход картинки принимает
Re[10]: Определение, вызов
От: Temoto  
Дата: 07.04.11 13:52
Оценка: :)
B>>исходниик в пейнте редактировать?
B>>Браво.
B>>ту же все рассказывают, что IDE всё подсветит, покажет.

A>Отредактировать исходник в пейнте — фигня вопрос. Покажите мне лучше компилятор, который на вход картинки принимает


http://www.dangermouse.net/esoteric/piet.html
Re: Способ именования конструкторов и деструкторов
От: Lloyd Россия  
Дата: 07.04.11 14:19
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Учитывается понятность и внешний вид кода, отсутствие неоднозначностей, удобство рефакторинга и работы IDE и любые другие факторы, которые могут иметь место.


Первый. Конструктор — это просто функция, создающая объект. Почему для нее нужен отдельный способ именования?

Вот что действительно не нравится — так это синтаксис вызова одного конструктора из другого, через this(...). Constraint new в дженениках тоже не нравится. Все в разнобой как-то.
Re: Способ именования конструкторов и деструкторов
От: x-code  
Дата: 07.04.11 19:51
Оценка:
Здравствуйте, x-code, Вы писали:

Мой вариант, критикуйте
Каждый метод предваряется некоторым ключевым словом: def — для впервые объявленных функций, redef — для перегруженных, можно подобрать ключевые слова для виртуальных и переопределенных (virtual, override) и т.п. Примерно так:
def Func1() int {}
redef Func2(int x, y) char {}

ctor и dtor — ключевые слова такого же типа для конструкторов и деструкторов.
Если любой метод обязан иметь имя, то методы ctor и dtor могут как иметь произвольные имена, так и не иметь их. Имена, если они есть, подчиняются общим правилам именования методов. Опциональная возможность именования конструкторов и деструкторов дает дополнительную возможность самодокументирования кода. Варинат с именами:
ctor InitDefault() {}
ctor InitFromStr(string s) {}

или по-старинке:
ctor() {}
ctor(string s) {}

если имя есть, то на функцию-конструктор можно сослаться, можно вызывать ее для переинициализации объекта, взять ее адрес, можно вызвать один конструктор из другого и т.д.

В вызывающем коде инициализация классическая
new MyClass();
new MyClass("my text");


Имя класса освобождается от дополнительной нагрузки. Если предположить, что мы имеем дело с языком, в котором каждая программная сущность — объект, то снимается неоднозначность: имя класса — уникальный идентификатор объекта класса, имя конструктора — уникальный идентификатор объекта-функции.
Re[2]: Способ именования конструкторов и деструкторов
От: Кодёнок  
Дата: 08.04.11 06:25
Оценка: +1
Здравствуйте, x-code, Вы писали:

XC>new MyClass("my text");[/code]


Если уж на то пошло, то оператор new должен иметь вид

new<MyClass>("my text");


а destroy должен требовать скобок

[/c]
destroy(foo);
[/c]
Re[3]: Способ именования конструкторов и деструкторов
От: x-code  
Дата: 08.04.11 08:05
Оценка:
Здравствуйте, Кодёнок, Вы писали:

Кё>Если уж на то пошло, то оператор new должен иметь вид


Кё>
Кё>new<MyClass>("my text");
Кё>


Кё>а destroy должен требовать скобок


Кё>
Кё>destroy(foo);
Кё>


А почему так?
Re: Способ именования конструкторов и деструкторов
От: maykie Россия  
Дата: 08.04.11 08:23
Оценка:
Здравствуйте, x-code, Вы писали:




XC>3. отдельные ключевые слова = функции с предопределенными именами

XC>class MyClass
XC>{
XC> ctor();
XC> dtor();
XC>};

XC>Учитывается понятность и внешний вид кода, отсутствие неоднозначностей, удобство рефакторинга и работы IDE и любые другие факторы, которые могут иметь место.


Третий. Первый противоречит DRY, второй из-за того что если в языке всё передаётся по ссылке и разрешена перегрузка операторов, то будет неоднозначность с operator():
class Callback<X>{
operator (X x) {fp(x)};
this(FuncPointer fp)
{
    this.fp = fp;
}

this(Callback<X> other)
{
     this(other.fp); //вызов оператора() или конструктора?
}

в третьем неоднозначностей не будет.
Re[2]: Способ именования конструкторов и деструкторов
От: hardcase Пират http://nemerle.org
Дата: 08.04.11 13:43
Оценка:
Здравствуйте, maykie, Вы писали:

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


При условии что в языке есть operator()
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Способ именования конструкторов и деструкторов
От: maxkar  
Дата: 08.04.11 18:41
Оценка: 5 (1)
Здравствуйте, x-code, Вы писали:

XC>Мой вариант, критикуйте

Без проблем
Модельный пример — прямоугольник. Публичное API ниже
class Rectangle {
  public function getLeft() : int;
  public function getRight() : int;
  public function getTop() : int;
  public function getBottom() : int;
  public function getWidth() : int;
  public function getHeight() : int;
  public function expandWidth(amount : int) : Rectangle; // Вовзращает прямоугольник с шириной, увеличенной на amount
}

Конструкторы специально не указаны. Мы их писать будем

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

  1. Instance initialization method — инициализация экземпляра. Выполняет следующие действия:
    • Инициализация полей класса по-умолчанию
    • Выполнение Instance initialization method предка (предков)
    • Выполнение произвольного User-defined кода
  2. Instance creation method — создание экземпляра. Выполняет следующие действия:
    • Выделяет память под экземпляр класса
    • Инициализирует таблицу виртуальных методов
    • Вызывает initialization method
Вызов Instance initialization и instance creation выглядит по-разному. Вообще, вызов других instance intialization доступен только в друих методах инициализации. Ну а вызов instance creation — это new <initialization method>(args). Разница в том, что instance initialization ничего не возвращает, а instance construction в результате возвращает новый объект. Это потребуется дальше.

XC>Каждый метод предваряется некоторым ключевым словом: (skip)

A!!! Блин, они гвоздями к объекту прибиты? Ну неудобно это. И я против сегрегации методов. Возьмем тот же прямоугольник. Не, мало. Массив прямоугольников! rectangles. И отконвертируем их в массив высот этих прямоугольников. Напишем, Array.map(rectangles, ???). И что туда писать? Анонимную функцию? Или Rectangle.getHeight? Кстати, кто такой тогда будет Rectangle.getHeight?
Зато если написать внешнюю функцию RectangleUtils.getWidth(rectangle : Rectangle) : int, ее можно туда передать.

Второй пример на то же самое. Теперь мы будем не ширину получать, а expand'ить один прямоугольник на массив ширин (и получать массив прямоугольников). В этом случае, казалось бы, все хорошо:
Array.map(extents, rectangle.expandWidth). Но вот незадача, нет в классе возможности увеличить прямоугольник по вертикали. Ну напишем метод RectangleUtils.expandHeight(rectangle : Rectangle, amount : int) : Rectangle. Но вот в Array.map(extends, ?) ее не скормить . Вот почему такая сегрегация методов в зависимости от места определения? Не, если есть extension method, то, вероятно, все хорошо (хотя хз).

Без сегрегации методы не принадлежат объекту. Методы принадлежат "типу". Динамические (переопределяемые) методы — тоже типу, только механизм их выполнения сложнее. Тогда по определению получается, что методы не определяются в классе (а, например, в модуле). Это автоматически приравнивает рукописный expandHeight к "библиотечному" expandWidth. Ну и небольшой сахар для привязки к метода к экземпляру:
smth.method ====>
function(...args) {
  SmthType.method.apply([smth].concat(args));
}


Кстати, проблема с необходимостью отвязки/привязки метода к экземпляру как раз реальная, я ее периодически на практике вижу. Жить то можно, но не красиво как-то.

К чему все это. Если методы принадлежат типу (а не экземплярам), видимо, "конструкторы" тоже принадлежат типу. И точно так же, как с методами, хочется прекратить сегрегацию instance creation method, объявленных рядом с классом и объявленных где-то еще. Здесь я уже веду речь именно об instance creation, так как в основном именно это интересует программиста. И специально делить такие методы на два класса (конструкторы с ответственностями выше) и "просто методы создания" мне кажется плохой идеей.

XC>Если любой метод обязан иметь имя, то методы ctor и dtor могут как иметь произвольные имена, так и не иметь их. Имена, если они есть, подчиняются общим правилам именования методов.

Это хорошая идея.

XC>если имя есть, то на функцию-конструктор можно сослаться, можно вызывать ее для переинициализации объекта, взять ее адрес, можно вызвать один конструктор из другого и т.д.

Не а, нельзя . Как я показывал выше, есть instance creation method и instance initialization method. И нужно уметь их различать. Видимо, у них все же должны быть разные имена. Вот что такое MyClass.InitFromStr — это создание объекта или его инициализация? Если это создание объекта, такую функцию нельзя вызвать для переинициализации (она новый объект возвращает). А если это функция инициализации, то ее невозможно вызывать для создания объекта. Что-то не получается... Ну и та же проблема с вызовом конструкторов — не ясно, вызвыается конструктор или метод инициализации.

XC>Опциональная возможность именования конструкторов и деструкторов дает дополнительную возможность самодокументирования кода.

XC>В вызывающем коде инициализация классическая
Переставил фразы оригинала, читаемость мне нужна здесь. Ну создаем новый прямоугольник:
  new Rectangle(1, 2, 3, 4);

Упс... Что такое 3 и 4? Это ширина и высота или все же координаты второй точки? И, кстати, почему выбирая перегрузку по типам аргументов мы жестко выбираем вариант с щириной/высотой или с координатами точки? Ведь и ширина/высота, и координаты вершин вполне равноправны. Ну ладно, нужно давать возможность создавать объекты указывая имена "конструкторов". В этом случае мы можем указать new Rectangle.bySize(1, 2, 3, 4); или new Rectangle.byCorners(1, 2, 3, 4). Но вот опять незадача. В библиотеке (определении выше) оказался только Rectangle.bySize. А вот byCorners не оказался, а нам часто нужен именно он. Казалось бы, мелочи. Пишем RectangleUtils.rectangleByCorners(...) : Rectangle. И получаем два instance creation method: new Rectangle.bySize и RectangleUtils.rectangleByCorners(...). Блин, ну вот почему опять мы получили два метода, которые вызываются по-разному??? Причем они с точки зрения прямоугольника вполне равноправны (чем думал автор библиотеки — умолчим).

XC>Имя класса освобождается от дополнительной нагрузки. Если предположить, что мы имеем дело с языком, в котором каждая программная сущность — объект, то снимается неоднозначность: имя класса — уникальный идентификатор объекта класса, имя конструктора — уникальный идентификатор объекта-функции.


Почти хорошо. Только вот в ваших примерах выше имя конструктора получается привязано к классу (ну и имени класса). Т.е. сам по себе "объект-функция" как-то не очень существует. Еще раз повторю — идея различения функций по месту их определения мне не очень нравится. Да, это влияет на поиск имен. Но давайте привяжем поиск имен не к "классам", а к модулям. Импортируются модули, модуль может определять несколько типов, методы для разных типов и т.п. Т.е. множество доступных имен определяется множеством открытых (импортированных) модулей, но никак не классов (типов на самом деле). Если допустить еще возможность переопределения по типу возвращаемого значения, получается все очень красиво и на синтаксическом сахаре:
new Rectangle.bySize(...) ====>
[bySize returning Rectangle](...)

Ну правда же, меня не очень интересует, кто и в какой момент будет аллоцировать память. Либо вызов метода, либо метод делегирует вызов куда-то еще.
На самом деле получается еще интереснее. Пусть есть RectangleFactory с методом createRectangle. Ну и сахар на вызов instanceMethod выше. Тогда (предположим, конфликтов и других перегрузок методов нет)
new Rectangle.createRectangle(rf) ===>
[createRectangle returning Rectangle](rf) <====
rf.createRectangle()

Как вам такая конструкция

Чуть не забыл, конструкторы без имен. Тоже можно через сахар сделать:
class Rectangle {
  ctor (...args)
} ====>

class Rectangle {
  ctor new(...args) : Rectangle {...}
}

var x = Rectangle.new(....);


P.S. Вообще, если позволить вводит "внешнее" отношение is-a между типами и сделать "явное" переопределение методов (т.е. через передачу в конструктор, например) получаются очень интересные возможности. В частности, становится не нужно понятие "implements interface". И вроде бы с системой модулей для видимости имен плюс сахар выше (и еще чуть-чуть нового) получить все то, что дает современное "ООП". Там, правда, есть некоторые проблемы с identity и необходимым сахаром для того, чтобы переопределение методов было удобным, но если интересно — могу рассказать подробнее.
Re[3]: Способ именования конструкторов и деструкторов
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 08.04.11 19:37
Оценка:
M> если интересно — могу рассказать подробнее.

интересно. расскажи, пожалуйста
Re[3]: Способ именования конструкторов и деструкторов
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 08.04.11 19:49
Оценка:
M>В этом случае мы можем указать new Rectangle.bySize(1, 2, 3, 4); или new Rectangle.byCorners(1, 2, 3, 4).

с точки зрения DRY лучше делать:
new Rectangle(1, 2, Size(3,4))
new Rectangle(Corner(1,2), Corner(3,4))


М> Вот что такое MyClass.InitFromStr — это создание объекта или его инициализация? Если это создание объекта, такую функцию нельзя вызвать для переинициализации (она новый объект возвращает).


инициализация объекта с поведением по умолчанию: объект — создается, а не переинициализируется
поведение по умолчанию может быть переопределено на другое по месту при необходимости.
в C++ кстати именно такая парадигма заложена.
Re[4]: Способ именования конструкторов и деструкторов
От: maxkar  
Дата: 09.04.11 16:17
Оценка: 25 (4)
Здравствуйте, DarkGray, Вы писали:

DG>интересно. расскажи, пожалуйста


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

В классических языках ООП решает слишком много задач, при этом не предоставляет средств для решения подобных задач. Решаемые задачи:
1. Установление отношения is-a между типами.
2. Обеспечение повторного использования реализации.
3. Полиморфизм поведения объектов одного и того же типа.
4. Управление доступом к различным методам, работающим над типом.
Проблема. Невозможно использовать каждый механизм по-отдельности. Кроме того, методы, работающие с одним и тем же типом вызываются совершенно по-разному в зависимости от того, определены они в самом «классе» или вне его (это я выше писал, с конструкторами та же проблема).

Первое и основное. Отношение is-a между типами. Вообще, на практике тип важен для того, чтобы определить, какие методы можно применять к объекту. Отношение A is-a B в данном случае определяет, что методы, применяемые к типу B могут применяться и к типу A. Именно это определяет и наследование, и реализация интерфейсов. Проблема в том, что подобное отношение формируется на этапе компиляции и в случае библиотек не может быть изменено. Хотя, например, хотелось бы сказать, что библиотечный String is-a каким-либо внутренним CharSequence. На практике для такой конвертации используется шаблон Adapter. Наличие целого шаблона говорит, что проблема не редкая. И наличие такого StringCharSequenceAdapter фактически говорит, что String may-be CharSequence by StringCharSequenceAdapter, только конвертацию нужно выполнять вручную в каждом месте использования. Почему бы не сказать компилятору
declare String is CharSequence with 
    function str → new StringCharSequenceAdapter(str)

В этом случае компилятор при необходимости будет создавать «конвертер» из одного типа в другой. Следует отметить, что это не приведение типа с созданием нового значения, это создание «обертки» над существующим объектом. В случае модификации одного из объектов второй соответствующим образом модифицируется. Если же требуется представлять строку двумя способами, придется все же в одном из случаев писать вызов метода, но такое происходит гораздо реже, чем простая конвертация.
При такой автоматической конвертации получается проблема — теряется identity объекта. Новосозданный объект отличается от исходного. Правда и в случае адаптера identity уже потерялась, но в данном случае потеря будет происходить чаще. Мне это не кажется проблемой, на практике я встречал только сравнение identity для объектов одного и того же типа. А для такого сценария проблема может быть решена компилятором — адаптер из одного типа в другой создается при первом использовании, а затем в качестве адаптера будет браться уже созданный экземпляр.

Теперь об интерфейсах. Что такое интерфейс в общем случае? Это фактически набор каких-либо методов. Собственно, это и есть запись (record), у которой все элементы являются функциями. Принципиально есть два варианта их применения. Первый — скормить экземпляр интерфейса в какую-нибудь библиотеку. Второй — использовать внутри программы для ограничения доступного списка методов. В обоих случаях, например,
declare FileOutputStream is OutputStream with 
    function stream → {
   close = close stream
   write = write stream
   ...
}
Ну или, с сахаром
declare FileOutputStream is OutputStream auto by-name

Помимо возможности введения внешних отношений, имеется также и возможность реализовать разные интерфейсы с конфликтующими именами методов (это не та проблема, которую стоит решать, но ее решение является бонусом).

Так что интерфейс — это фактически обычный тип. Еще один плюс — на record-style интерфейсах гораздо проще писать различные конвертеры. Фактически, конвертер выглядит как
let wrapSomeMethod iface = iface with { someMethod = wrap (iface.someMethod) }

По сравнению с полноценной реализацией новой обертки как-то гораздо красивее смотрится .

Можно автоматически генерировать "accessor methods" для интерфейса:
interface type Z = {methodA : int -> String; methodB : String -> int }
будет еще генерировать
let methodA (z : Z, i : int) -> z.methodA(i)
let methodB (z : Z, s : String) -> z.methodB(s)

Хотя если вызвать в стиле z.methodA(...), то такой генерации и не понадобится (для экземпляров классов это раскручивается в вызов метода с параметром-объектом).

Второе. Использование реализации. Обычная композиция. Ну с отношением is-a выше здесь вообще все похоже на классический ООП.
type derived = record
    base : parent,
    … какие-то еще данные
end

declare derived is base with 
    function der → der.base


Можно и сахар для одновременного объявления типа и конвертора сделать. Бонус:
type IOStream = record
  base : Stream
  inputStream : Istream
  outputStream : Ostream
end

declare IOStream is Stream with
    function str → str.base
delcare IOStream is Istream with
    function str → str.inputStream
declare IOStream is Ostream with
    function str → str.outputStream

let createFileIOStream fname = 
  let baseStream = createFileStream fname in
  { base : baseStream,
     inputStream : createInputStream(baseStream),
     outputStream : createOutputStream(baseStream)
  }


Можно сделать и отдельные экземпляры с двумя base. В этом случае неявно преобразовать IOStream в Stream не получится (конфликт различных преобразований). Да и плохая это будет идея, так как IOStream в этом случае все же не Stream а целых два Stream.

В качестве минуса — можно написать так, что понять, почему и как объект привелся к типу. На практике не видел, где могла бы возникнуть такая цепочка. Зато в качестве плюса можно сделать отношение is-a локально в каком-то фрагменте (хотя в этом случае identity точно потеряется и на при необходимости будет вызываться метод конвертации). Фактически, это отношение работает только в compile-time и служит для выбора метода, который будет вызван.

Третье. Полиморфизм. Это тоже не сложно. Правда предлагаю делать полиморфмизм явным. В любом случае, для изменения поведения какого-либо метода класса требуется явно передать требуемый метод. Ну просто потому, что для корректного переопределения требуется знать, в каких внутренних методах используется переопределяемый метод. Например, приведет ли переопределение метода Rectangle.getWidth() к изменению поведения метода Rectangle.getArea()? Еще одна проблема возникает, если нужно в различных комбинация переопределять поведение. Что интересно — при этом фактически новых типов не возникает — все методы совпадают с методами родителя, класс сейчас нужен только для того, чтобы реализация работала. Если явно (правда используется динамика):
let createSomethingWithA args = createSomething args { methodA : … }
let createSomethingWithB args = createSomething args { methodB : … }
let createSomethingWithAandB args = createSomething args { methodA : …; methodB : … }


Можно и более сложные случаи, когда createSomethingWithA тоже получает список «переопределений», как-то модифицирует его и создает актуальных экземпляр. Методы, работающие с экземпляром, просто обращаются к соответствующим полям. Если честно, полиморфизма на таком уровне мне было не нужно. Мне был нужен полиморфизм на уровне «в частных случаях переопределить поведение пары методов». И работал он на уровне тех самых интерфейсов в record-style. Т.е. можно было бы создать default implementation и затем заменить пару методов. Может быть, когда-нибудь так и сделаю. Там обработчики событий были

Для доступа к «защищенным» полям передавать соответствующие «интерфейсы». Собственно, плюс интерфейсов в стиле «record» здесь и проявляется. Адаптер в стиле record гораздо короче, чем «полноразмерный» класс.
Let callProtected x = x.protMethod {
  dispose : x.dispose
  draw : x.draw
}

Может быть еще определение этого типа. В принципе, все это можно делать и сейчас, но в mainstream код такой обертки гораздо тяжелее.

Ну и последнее. Управление доступом. При полиморфизме (переопределяемые protected methods) это все делается вручную и явно. Все остальное делается модульной системой (что-то вроде модулей в ocaml). Они определяют, какие методы в какой момент видны. Ну а дальше уже механизм разрешения методов. Плюс сахар для того, чтобы методы можно было брать как instance method, так и извне. Вероятно, для удобства написания методов потребуется и конструкция
with something {
  // здесь методы сначала разрешаются как методы или поля something и только потом из внешнего контекста.
}
ну и сахар 
defmethod method Type (args …)  … =>
let method instance args …
    with instance
        ….
.

Собирая все вместе.
//Определение класса
class x === type t
interface Y === type Y = record {
  methodA : <type>;
  methodB : <type> ...
}

//Конструторы
public X(...) === export createX(...)
internal/private x(...) === createX(...) //внутренний для модуля

// Наследование
Z extends X === { super : X, ... } 
и declare Z is-a X with function z -> z.super

приватное "наследование". Точно так же, но без declare Z is-a ...

в конструторе
{ ... super(a, b, c) ... }  === let super_instance = createX(a, b, c) ... { super : super_instance, ...}

// реализация интерфейса
Z implements Y ===
declare Z is-a Y with function z -> { methodA : z.methodA, methodB : z.methodD, ...}
или
declare Z is-a Y auto by-name // в рамках видимых в данном контексте методов

// методы
public function zzz (args) {...} ===

let function zzz this : Z args =
  with this { ...
  }

"Extension method" выглядит точно так же, только находится в другом модуле.

//переопределение методов
override function zzz (args) { ... } ===
явно через конструтор:

type X {
  overrides : ...
}

let createX(constructuor-args, overrides) = { overrides : concatOverwrides(overwrides, DEFAULT_IMPLEMENTATIONS), .... }

// без наследования. Обычно если нужно переопределять методы, типы менять не нужно.
let createZ(constructuor-args, overrides2) = createX(constructor-args, concatOverwrired(overries2, DEFAULT_Z_IMPLEMENTATIONS))

Бонусы конвертеров интерфейсов показал. Создание "экземпляров" интерфейсов тоже удобнее. Можно сложные варианты instance creation method делать (по сути это обычная функция). А вообще все это борьба с классами, которые упоминаются только в operator new а дальше используются с типом интерфейсов или своих базовых типов. Очень много их них заменяется на instance creation method с замыканиями. Это и реализации интерфейсов, и достаточно большая часть override protected. Вот. Именно это мне и нужно. Даже не смотря на небольшие проблемы с indentity. А если что-то еще из "ООП" осталось, им я даже готов пожертвовать так как не пользуюсь. Хотя примеры рассмотрел бы.
Re[2]: Способ именования конструкторов и деструкторов
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.04.11 01:58
Оценка:
Здравствуйте, maykie, Вы писали:

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

M>
M>class Callback<X>{
M>operator (X x) {fp(x)};
M>this(FuncPointer fp)
M>{
M>    this.fp = fp;
M>}

M>this(Callback<X> other)
M>{
M>     this(other.fp); //вызов оператора() или конструктора?
M>}
M>

M>в третьем неоднозначностей не будет.

Ничерта не понял. Что за байда у тебя с этим оператором? И какая ему разница с чем конфликтовать с this или с ctor?

В Nemerle используется this и есть перегрузка операторов. Никаких конфликтов не наблюдается. Более того this используется еще в ряде контекство, но опять таки никаких проблем не видно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Способ именования конструкторов и деструкторов
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.04.11 02:01
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>всегда только IDE?

B>Можно вообще уйти от фиксированных имён и аннотировать методы.
B>Что-то типа @Constructor. Тогда без IDE будет полный пипец. А такие ситуации довольно часты.

Полный пипец это когда люди одну логику применяют для конструкторов, а другую для методов. По вашей логике методы надо тоже называть так чтобы из их имени было ясно к какому типу они относятся. Но почему-то это вас не волнует.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Способ именования конструкторов и деструкторов
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.04.11 02:11
Оценка: +2 -1
Здравствуйте, DarkGray, Вы писали:

DG>чтение кода с любого места в произвольном направлении (обычно конфликтует с критерием дублирование информации)

DG>полезно при беглом чтении кода, анализе, частичном автоматизированном разборе и т.д.
DG>конструктор/деструктор могут быть достаточно отдалены от объявления самого класса, а для названий this/~this, ctor/dtor вообще нет никаких подсказок о каких конструкторах/деструкторах идет речь, что требует либо аккуратный скроллинг к объявлению класса (при беглом скроллинге можно перескочить на чужое объявление), либо подсказок от IDE. с методами проблема менее критична, т.к. название метода уже часто несет большую часть информации — что это за метод, и название класса необходимо только когда метод реально полиморфный.

Очередной пример извращенной логики теоретиков.

Заметь! Конструкторов в типах обычно очень не много. А вот методов туча! И почему-то тебя не волнует то, что эта туча не имеет "онтекста".

Логика твоя высосана из пальца. На практике никто никогда не читает код случайным образом ткнув мышью в файле.

Когда код пишется, то контекст понятен сам собой.

Когда код меняется, то сначала человек ищет тип и уже потом ищет в нем конструкторы (если ему нужно читать/менять именно их). Так что контекст ему опять же становится известен.

У имени совпадающего с именем типа есть одно преимущество — можно тупо нажать на Ctrl+F3 в студии и найти то же имя ниже в файле. Но это наличие в IDE возможности открыть список членов типа и перейти к нужному полностью покрывают эту "удобность". Так что преимущество весьма спорное.

А вот с точки зрения именно чтения куда как удобнее иметь стандартное имя для конструкторов. Это позволяет выделять их визуально, а значит не путать с простыми методами.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Определение, вызов
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.04.11 02:14
Оценка: +1
Здравствуйте, Qbit86, Вы писали:

Q>Топикстартер предоставил синтаксис определения, а не вызова. Вызов, я так полагаю, по прежнему будет new MyClass(params).


"new", кстати, тоже лишнее
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Определение, вызов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.04.11 02:52
Оценка:
Здравствуйте, VladD2, Вы писали:

Q>>Топикстартер предоставил синтаксис определения, а не вызова. Вызов, я так полагаю, по прежнему будет new MyClass(params).

VD>"new", кстати, тоже лишнее

public class MyClass
{
}

public class YourClass
{
    private static MyClass MyClass()
    {
       return null;
    }

    public YourClass()
    {
        // Syntax without 'new' keyword.
        // Is this invocation of Method or construction of object?
        MyClass x = MyClass();
    }
}
A journey of a thousand miles must begin with a single step © Lau Tsu
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.