Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 13:23
Оценка: 48 (5) +4 :)
Идея инспирирована вот этой темой
Автор: IDL
Дата: 05.10.09
, анонимными типами C# и списком аргументов.

Для начала пояснения.

Как рассматриваются кортежи (tuple-ы) в функциональных языках (ФЯ)? Не скажу за все языки (хотя почти уверен, что в других ФЯ все обстоит сходным образом), но в Nemerle кортеж — это не просто структура данных. Кортеж — это часть дизайна языка. Дело в том, что в немерле (далее, для понимания, можно смело мысленно заменять немерле на ФЯ) функция является очень важным объектом. А раз функция — это объект, то у нее должен быть тип. Так вот, тип функции описывается как "X -> Y" (без кавычек), где X и Y — это типы. X — это тип аргументов, а Y — возвращаемого значения. Заметьте, в этом описании нет списка аргументов! Аргумент один! Это не случайно. Под этим есть не хилая теоретическая основа лямбда-исчесления Чёрча. Но язык тем не менее поддерживает функции с более чем одним аргументом. Как же это возможно?

Наличие функций с более чем одним параметром возможно благодаря кортежам. Если функция принимает два параметра один из которых строка, а второй целое, то такая функция записывается как "string * int -> Y". Таким образом типом параметров такой функции является кортеж "string * int". Логично, что и возвращаемое значение может быть так же кортежем, например, "string * int -> string * int".

В чем же проблема? Проблема в том, что простые смертные оперируют конкретикой. Для них мало абстрактных типов аргументов и возвращаемого значения. Им нужны еще названия параметров. Название параметров локально для функции в которой они объявлены (для ее реализации). По сути программист думает о списке параметров функции как о наборе локальных переменных.

Теперь, собственно предложение.

А почему бы не рассматривать возвращаемое значение функции так же как набор параметров? При этом синтаксис может быть совершенно разрообразным. Например, мы можем объявлять функцию так:
Гипотетический язык:
/*in parameters*/(string param1, int param2) FuncName /*out parameters*/(string ret1, int ret2)
{
}

/*in parameters*/(string param1) FuncName /*out parameters*/(int ret2)
{
}

Расширение C#:
(string ret1, int ret2) FuncName(string param1, int param2)
{
}

(string ret1) FuncName(int param2)
{
}


Кроме того в шарпе можно использовать следующее соглашение. Функцию описанную так:
string FuncName(string param1, inout int param2)
{
}

можно рассматривать как функцию с типом: string * int -> string * int и соответственно применять без передачи параметра по ссылке.
А функцию:
string FuncName(string param1, out int param2)
{
}

можно рассматривать как функцию с типом: string -> string * int и соответственно применять:
var (str, num) = FuncName("abc");

Расширение немерле:
FuncName(param1 : string, param2 : int) -> (ret1 : string, ret2 : int)
{
}

(string ret1, int ret2) FuncName (string param1, int param2)
{
}
[/c#]

Так как в таких сигнатурах имеются имена "полей", то конструкция декомпозиции (опять же обращаемся к немерле):
def (var1, var2, var3) = f();

можно обеспечить дополнительный контроль и поддержку IDE.
Кроме того можно будет будет использовать следующий синтаксис:
def x = f();
... = x.var1 + x.var2;
... x.var3

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

Сам тип при этом остается совместимым с кортежем и может передаваться всюду где используется обычные кортежи (с не именованными полями).

Такое решение подойдет и для языков (точнее их рантаймов) в которых есть поддержка записей, и для рантаймов вроде дотнета где по недомыслию разработчиков типа "запись" нет. Причем для вторых такое решение будет даже более нужным, так как оно позволяет улучшить языки работающие на этой платформе без изменения самой платформы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 10.10.09 13:58
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Наличие функций с более чем одним параметром возможно благодаря кортежам. Если функция принимает два параметра один из которых строка, а второй целое, то такая функция записывается как "string * int -> Y". Таким образом типом параметров такой функции является кортеж "string * int". Логично, что и возвращаемое значение может быть так же кортежем, например, "string * int -> string * int".


VD>В чем же проблема? Проблема в том, что простые смертные оперируют конкретикой. Для них мало абстрактных типов аргументов и возвращаемого значения. Им нужны еще названия параметров.


Проблема не только в этом. А так же в том, что запись string*int->Y не совсем соответствует действительности для платформы .NET. В немерле так принято записывать только потому что string->int->Y еще больше не соответсвует действительности.
Допустим, что мы имеем функцию string * int -> string * int, которая принимает "якобы" кортеж (а на самом деле два аргумента через запятую), а возвращает именно кортеж. Проблема в том, что чтобы подать результат функции на ее же вход, потребуется кортеж разделить на части и подать их по-частям. Очевидно, что мы не можем записывать одинаково то что физически является разными вещами (вход и выход).
Т.е. если мы оставляем запись string*int для обозначения кортежа, то функция типа string*int->string*int должна как принимать именно кортеж, так и возвращать его.

Кроме того, мы должны уметь передавать кортежи в функцию, и передача (string, string * int) не должна автоматически преобразоваться в (string * string * int).

VD>Теперь, собственно предложение.


VD>Расширение C#:

VD>
VD>(string ret1, int ret2) FuncName(string param1, int param2)
VD>{
VD>}

VD>(string ret1) FuncName(int param2)
VD>{
VD>}
VD>


Я уже где-то недавно в шутку предлагал (возможно в той теме) вариант с возвратом из метода анонимного типа.
Что-то воде
{string ret1, int ret2} FuncName(string param1, int param2)
{
}

Т.е. хочется скрестить ужа с ежом и получить анонимный тип с позиционным доступом или тупл с именованными свойствами, причем с полной совместимостью по типу, невзирая на имена свойств.
И разрешить не только возвращать такие штуки, но и принимать их и объявлять в теле метода.

{ r1, r2 } = Func(p1, p2);

и

{ string ret1, int ret2 } FuncName(string param1, { string p1, int p2 })

Причем, результат совместим с типом второго аргумента функции.
Re: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 10.10.09 14:43
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Теперь, собственно предложение.


Справедливо стоит заметить, что предлагать начал Undying здесь
Автор: Undying
Дата: 06.10.09
. Правда, его предложение, хоть и с изменением синтаксиса, но было основано на расширении дженериков.
Мне же кажется, что надо идти не от туплов к анонимным типам, а от анонимных типов к туплам. И сделать-то надо небольшой шаг — зафиксировать позиции элементов, как в тупле и обеспечить совместимость с анонимными типами, имеющих аналогичные типы на тех же позициях.
Обращаться к свойствам анонимного типа даже удобнее, чем к элементам тупла:

var x = Foo(); // возвращает анонимный тип с сигнатурой, вынесенной в сигнатуру метода Foo()
Console.WriteLine(x.a);

vs
{ a1, b1 } = Foo();
Console.WriteLine(a1);
Re: Inline records
От: Nuzhny Россия https://github.com/Nuzhny007
Дата: 10.10.09 14:48
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Идея инспирирована вот этой темой
Автор: IDL
Дата: 05.10.09
, анонимными типами C# и списком аргументов.

VD>...
VD>А почему бы не рассматривать возвращаемое значение функции так же как набор параметров? При этом синтаксис может быть совершенно разрообразным.

В lua функция может возвращать несколько значений. И я не думаю, что это какая-то инновационная идея.
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 15:23
Оценка: +2
Здравствуйте, samius, Вы писали:

S>Проблема не только в этом. А так же в том, что запись string*int->Y не совсем соответствует действительности для платформы .NET. В немерле так принято записывать только потому что string->int->Y еще больше не соответсвует действительности.


-1

S>Допустим, что мы имеем функцию string * int -> string * int, которая принимает "якобы" кортеж (а на самом деле два аргумента через запятую), а возвращает именно кортеж. Проблема в том, что чтобы подать результат функции на ее же вход, потребуется кортеж разделить на части и подать их по-частям.


Не по требуется. Точнее не требуется. Язык "работает" на уровне своих абстракций. В немерле кортеж подходящего типа можно передать в функцию без проблем.
Ну, а что там происходит на физическом уровне это дело десятое. В прочем, и на физическом уровне расположение аргументов с стеке и кортежа полностью совпадают.

S>Очевидно, что мы не можем записывать одинаково то что физически является разными вещами (вход и выход).


Очевидно, что твоя теория неверна, в следствии не верности предпосылок.

S>Т.е. если мы оставляем запись string*int для обозначения кортежа, то функция типа string*int->string*int должна как принимать именно кортеж, так и возвращать его.


Она так и делает. Вот так записывается конструктор кортежа:
("aaa", 12)

а вот так вызов функции:
f("aaa", 12)

найдите 10 различий.

S>Кроме того, мы должны уметь передавать кортежи в функцию, и передача (string, string * int) не должна автоматически преобразоваться в (string * string * int).


Нет никаких "string, string * int" есть описание типа в котом всегда используются "*" для отделения типов, и описание конструктора кортежа в котором всегда используются запятые в качестве разделителей. Но это все условности и ни что не мешает описывать типы так же через запятую.

S>Т.е. хочется скрестить ужа с ежом и получить анонимный тип с позиционным доступом или тупл с именованными свойствами, причем с полной совместимостью по типу, невзирая на имена свойств.


Ёж и уж видны только тем, кто кроме ООП в своей жизни больше ничего не видел. Практически во всех языках отличных от С++ и его наследников есть такие типы как кортеж и запись, причем кортеж считается записью у которой отсутствуют имена полей. Так что это совершенно нормальная вещь.

А "с полной совместимостью по типу, невзирая на имена свойств" называется структурной эквивалентностью. Она тоже есть почти во всех языках, даже в С++ (но не в Яве и Шарпе).
Так вот для анонимных типов структурная эквивалентность не поддерживается. Причем это проблема CLR.
Я же предлагаю не объявление нового типа, как кому-то тут показалось, а аннотацию кортежа. Мы же не говорим, что параметры функции — это анонимный тип только на основании того, что у них есть имена? Вот тут та же фигня. Имена всего лишь дополнительная аннотация не вводящая новый тип, но повышающая удобство на практике.

S>И разрешить не только возвращать такие штуки, но и принимать их и объявлять в теле метода.


Это идея здравая. Например, если у нас есть функция типа:
void f(int x, List<int * string> xs)

то было бы не плохо записать ее в более ясном виде:
void f(int x, List<int x, string name> xs)


S>
S>{ r1, r2 } = Func(p1, p2);
S>

S>и

S>
S>{ string ret1, int ret2 } FuncName(string param1, { string p1, int p2 })
S>

S>Причем, результат совместим с типом второго аргумента функции.

1. Зачем тут фигурные скобки? Все равно это не синтаксис анонимных типов.
2. Для соместимости анонимных типов нужна поддержка CLR. Иначе ничего не получится, если функция будет объявлена в одной сборке, а анонимный тип в другой.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 15:29
Оценка: 1 (1)
Здравствуйте, samius, Вы писали:

S>Мне же кажется, что надо идти не от туплов к анонимным типам, а от анонимных типов к туплам.


Вам Майкрософт мозг вы...л своими названиями. То что МС (и ты) называешь анонимными типами давным довно называлось записями и являлось разновидностью кортежей с именованными полями. Вся теория реляционных БД на этом построена.

Так вот анонимные типы — это недоразумение возникшее в следствии того, что в МС вместо грамотного проектирования всей системы (CLR и языков) решали частную проблему встраивания запросов в язык. Проблема этого неполноценного решения заключается в том, что в дотнет нет структурной эквивалентности, а без него тип (именно полноценный тип, а не синтаксический сахар, что бы встроен в шарп) создать нельзя.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 15:32
Оценка:
Здравствуйте, Nuzhny, Вы писали:

N>В lua функция может возвращать несколько значений. И я не думаю, что это какая-то инновационная идея.


Да пол языков может возвращать множество значений. Разговор был не о том. Речь идет об включении в описание функции имен этих отдельных значений.

А луа это динамически типизированный скрип в котором даже понятия такого как описание типа нет.

Прочитай еще раз написанное.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 10.10.09 18:22
Оценка:
Здравствуйте, VladD2, Вы писали:

S>>Допустим, что мы имеем функцию string * int -> string * int, которая принимает "якобы" кортеж (а на самом деле два аргумента через запятую), а возвращает именно кортеж. Проблема в том, что чтобы подать результат функции на ее же вход, потребуется кортеж разделить на части и подать их по-частям.


VD>Не по требуется. Точнее не требуется. Язык "работает" на уровне своих абстракций. В немерле кортеж подходящего типа можно передать в функцию без проблем.

Не знал.
Но в четвертом дотнете есть вполне определенный тип Tuple<,,,>, который в том же цэ-шарпе без проблем не залезет в функцию, если не объявить ее параметры туплом.

VD>Ну, а что там происходит на физическом уровне это дело десятое. В прочем, и на физическом уровне расположение аргументов с стеке и кортежа полностью совпадают.

Только для кортежа? Или ничего не стоит передать Rectangle вместо 4х интов?

S>>Очевидно, что мы не можем записывать одинаково то что физически является разными вещами (вход и выход).


VD>Очевидно, что твоя теория неверна, в следствии не верности предпосылок.

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

S>>Т.е. если мы оставляем запись string*int для обозначения кортежа, то функция типа string*int->string*int должна как принимать именно кортеж, так и возвращать его.


VD>Она так и делает. Вот так записывается конструктор кортежа:

VD>
("aaa", 12)

VD>а вот так вызов функции:
VD>
f("aaa", 12)

VD>найдите 10 различий.

А как будет выглядеть передача кортежа в TryGetValue(TKey, out TValue)?

S>>Кроме того, мы должны уметь передавать кортежи в функцию, и передача (string, string * int) не должна автоматически преобразоваться в (string * string * int).


VD>Нет никаких "string, string * int" есть описание типа в котом всегда используются "*" для отделения типов, и описание конструктора кортежа в котором всегда используются запятые в качестве разделителей. Но это все условности и ни что не мешает описывать типы так же через запятую.

Когда есть поддержка языка — не мешает. КОгда нет, то речь идет об string, Tuple<string, int>! И это далеко не эквивалентно Tuple<string, string, int>.

S>>Т.е. хочется скрестить ужа с ежом и получить анонимный тип с позиционным доступом или тупл с именованными свойствами, причем с полной совместимостью по типу, невзирая на имена свойств.


VD>Ёж и уж видны только тем, кто кроме ООП в своей жизни больше ничего не видел. Практически во всех языках отличных от С++ и его наследников есть такие типы как кортеж и запись, причем кортеж считается записью у которой отсутствуют имена полей. Так что это совершенно нормальная вещь.

Обижаешь, видел. И знаю, что это нормальная вещь. И вот структурная эквивалентность кортежа и записи показалась мне неплохой идеей.

VD>А "с полной совместимостью по типу, невзирая на имена свойств" называется структурной эквивалентностью. Она тоже есть почти во всех языках, даже в С++ (но не в Яве и Шарпе).

Просто не знал названия явления.

VD>Так вот для анонимных типов структурная эквивалентность не поддерживается. Причем это проблема CLR.

Видимо потому, как не предполагался взгляд на анонимный тип как на позиционную структуру.

VD>Я же предлагаю не объявление нового типа, как кому-то тут показалось, а аннотацию кортежа. Мы же не говорим, что параметры функции — это анонимный тип только на основании того, что у них есть имена? Вот тут та же фигня. Имена всего лишь дополнительная аннотация не вводящая новый тип, но повышающая удобство на практике.

А кто предлагал вводить тип?

S>>И разрешить не только возвращать такие штуки, но и принимать их и объявлять в теле метода.


VD>Это идея здравая. Например, если у нас есть функция типа:

VD>
VD>void f(int x, List<int x, string name> xs)
VD>

Приписывание имен к дженерик аргументам — не моя идея, а Undying-а.

S>>
S>>{ r1, r2 } = Func(p1, p2);
S>>

S>>и

S>>
S>>{ string ret1, int ret2 } FuncName(string param1, { string p1, int p2 })
S>>

S>>Причем, результат совместим с типом второго аргумента функции.

VD>1. Зачем тут фигурные скобки? Все равно это не синтаксис анонимных типов.

VD>2. Для соместимости анонимных типов нужна поддержка CLR. Иначе ничего не получится, если функция будет объявлена в одной сборке, а анонимный тип в другой.

Ну вот а я полагал синтаксис анонимных типов как раз. Вместе с их совместимостью и разанонимированием.

Насколько я помню, после выхода C#3.0, разговоры о возможности передавать анонимные типы между сборками велись и фича вроде бы приурочивалась ко смене CLR. Если бы до этого дошло в C#4, то полагаю, их совместимость уже была бы. Но мы почему-то вместо свойств Item1, Item2, Item{K} у анонимных типов получили новые типы Tuple.
Re[3]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 10.10.09 19:00
Оценка:
Здравствуйте, VladD2, Вы писали:

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


S>>Мне же кажется, что надо идти не от туплов к анонимным типам, а от анонимных типов к туплам.


VD>Вам Майкрософт мозг вы...л своими названиями. То что МС (и ты) называешь анонимными типами давным довно называлось записями и являлось разновидностью кортежей с именованными полями. Вся теория реляционных БД на этом построена.


Я не пытаюсь дорабатывать хаскель, окамл или что-нибудь еще. Я ответил на твое предложение по развитию именно C#-а. Не знаю, как немерле, а тому же F#-у эти доработки вместе с анонимными типами вроде как и нафиг не сдались. Вот и ответил в терминах Майкрософта, выдуманных для C#-а.

VD>Так вот анонимные типы — это недоразумение возникшее в следствии того, что в МС вместо грамотного проектирования всей системы (CLR и языков) решали частную проблему встраивания запросов в язык. Проблема этого неполноценного решения заключается в том, что в дотнет нет структурной эквивалентности, а без него тип (именно полноценный тип, а не синтаксический сахар, что бы встроен в шарп) создать нельзя.


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

Ты же предлагаешь для C#-а третью сущность вслед за анонимными типами и туплами (в том виде, в котором они были вытащены из FSharp.Core), а именно то, как оно должно было бы быть, если бы об этом подумали до появления анонимных типов и рискнули бы сменить CLR еще тогда. Это хоть и правильно, но это уже не путь C#. А вот допилка анонимных типов — это все еще вариант (пока, может быть).

Да, в F#-е эти туплы хоть и имеют должную поддержку языка, но вызов метода с передачей кортежа вместо аргументов через запятую (как в немерле) и там не реален. Потому то меня и смутила форма записи типа кортежа со звездой приминительно к немерле (я ожидал от его кортежей чего-то подобного F#-у).
Re: Inline records
От: Lloyd Россия  
Дата: 10.10.09 19:18
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Кроме того можно будет будет использовать следующий синтаксис:

VD>
VD>def x = f();
VD>... = x.var1 + x.var2;
VD>... x.var3
VD>


Если я правильно понимаю, в этом случае одинаковые по сигнатуре (типы входных/выходных параметров) функции не будут совместимы?
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 19:59
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Если я правильно понимаю, в этом случае одинаковые по сигнатуре (типы входных/выходных параметров) функции не будут совместимы?


Да. Для немерел они и сейчас совместимы, т.е. если функция возвращает кортеж совместимый с параметрами другой функции, то можно вложить вызов первой во второй.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Inline records
От: Lloyd Россия  
Дата: 10.10.09 20:05
Оценка:
Здравствуйте, VladD2, Вы писали:

L>>Если я правильно понимаю, в этом случае одинаковые по сигнатуре (типы входных/выходных параметров) функции не будут совместимы?


VD>Да. Для немерел они и сейчас совместимы, т.е. если функция возвращает кортеж совместимый с параметрами другой функции, то можно вложить вызов первой во второй.


Кажется, ты не заметил "не" в вопросе.
Re: Inline records
От: thesz Россия http://thesz.livejournal.com
Дата: 10.10.09 20:26
Оценка: 6 (1) -2
Здравствуйте, VladD2, Вы писали:

VD>Идея инспирирована вот этой темой
Автор: IDL
Дата: 05.10.09
, анонимными типами C# и списком аргументов.


VD>Для начала пояснения.


VD>Как рассматриваются кортежи (tuple-ы) в функциональных языках (ФЯ)? Не скажу за все языки (хотя почти уверен, что в других ФЯ все обстоит сходным образом), но в Nemerle кортеж — это не просто структура данных.


Ты бы для разнообразия ознакомился бы с состоянием дел в других языках, что ли.

А то начал за здравие — кортежи в функциональных языках, — а закончил за упокой — в Nemerle так, про другие не знаю.

http://www.haskell.org/ghc/docs/latest/html/libraries/ghc-prim/GHC-Tuple.html

data (,) a b = (,) a b
data (,,) a b c = (,,) a b c
data (,,,) a b c d = (,,,) a b c d
data (,,,,) a b c d e = (,,,,) a b c d e
data (,,,,,) a b c d e f = (,,,,,) a b c d e f
data (,,,,,,) a b c d e f g = (,,,,,,) a b c d e f g
data (,,,,,,,) a b c d e f g h = (,,,,,,,) a b c d e f g h
data (,,,,,,,,) a b c d e f g h i = (,,,,,,,,) a b c d e f g h i
data (,,,,,,,,,) a b c d e f g h i j = (,,,,,,,,,) a b c d e f g h i j
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 23:07
Оценка: +1
Здравствуйте, Lloyd, Вы писали:

L>>>Если я правильно понимаю, в этом случае одинаковые по сигнатуре (типы входных/выходных параметров) функции не будут совместимы?


L>Кажется, ты не заметил "не" в вопросе.


Ты бы расшифровал свой вопрос. А то не ясно что ты вообще имел в виду.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 23:12
Оценка:
Здравствуйте, thesz, Вы писали:

T>Ты бы для разнообразия ознакомился бы с состоянием дел в других языках, что ли.


Для разнообразия ознакомился. А ты бы прежде чем вепендриваться с начало бы разобрался в сути вопроса.

T>А то начал за здравие — кортежи в функциональных языках, — а закончил за упокой — в Nemerle так, про другие не знаю.


Ну и за каким фигом ты эту ссылку дал? Тему то читал?

T>http://www.haskell.org/ghc/docs/latest/html/libraries/ghc-prim/GHC-Tuple.html


T>
T>data (,) a b = (,) a b
T>data (,,) a b c = (,,) a b c
T>data (,,,) a b c d = (,,,) a b c d
T>data (,,,,) a b c d e = (,,,,) a b c d e
T>data (,,,,,) a b c d e f = (,,,,,) a b c d e f
T>data (,,,,,,) a b c d e f g = (,,,,,,) a b c d e f g
T>data (,,,,,,,) a b c d e f g h = (,,,,,,,) a b c d e f g h
T>data (,,,,,,,,) a b c d e f g h i = (,,,,,,,,) a b c d e f g h i
T>data (,,,,,,,,,) a b c d e f g h i j = (,,,,,,,,,) a b c d e f g h i j
T>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Inline records
От: FR  
Дата: 11.10.09 05:24
Оценка: 1 (1)
Здравствуйте, samius, Вы писали:

S>Да, в F#-е эти туплы хоть и имеют должную поддержку языка, но вызов метода с передачей кортежа вместо аргументов через запятую (как в немерле) и там не реален. Потому то меня и смутила форма записи типа кортежа со звездой приминительно к немерле (я ожидал от его кортежей чего-то подобного F#-у).


В F# вызов функций с несколькими аргументами это просто сахар для карринга, поэтому никакой "совместимости" и не может быть. Но никто не запрещает (хотя это плохой стиль) использовать вместо функций с несколькими аргументами функцию с одним аргументом кортежом, тогда все будет отлично совместимо.
Re[3]: Inline records
От: FR  
Дата: 11.10.09 05:36
Оценка: 71 (3)
Здравствуйте, VladD2, Вы писали:

VD>Да. Для немерел они и сейчас совместимы, т.е. если функция возвращает кортеж совместимый с параметрами другой функции, то можно вложить вызов первой во второй.


В D тоже самое. Кроме того структуры и классы имеют свойство .tupleof и можно их одним вызовом отправлять в параметры функции при совпадении типов конечно:

struct Tst
{
    int x;
    int y;
}

void tst(int x, int y)
{
}

void main()
{
    Tst t1;
    tst(t1.tupleof);
}
Re[5]: Inline records
От: Lloyd Россия  
Дата: 11.10.09 07:16
Оценка:
Здравствуйте, VladD2, Вы писали:

L>>>>Если я правильно понимаю, в этом случае одинаковые по сигнатуре (типы входных/выходных параметров) функции не будут совместимы?


L>>Кажется, ты не заметил "не" в вопросе.


VD>Ты бы расшифровал свой вопрос. А то не ясно что ты вообще имел в виду.


VD>def x = f();
VD>... = x.var1 + x.var2;
VD>... x.var3


Под несовместимостью я имел в виду следующее: если есть функция g, которая отличается от f только именем возвращаемого параметра, то она не может быть использована в приведенном куске кода.
Re[5]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 11.10.09 07:25
Оценка: +1 :)
Здравствуйте, FR, Вы писали:

FR>Но никто не запрещает (хотя это плохой стиль) использовать вместо функций с несколькими аргументами функцию с одним аргументом кортежом, тогда все будет отлично совместимо.

Было бы странно, если бы нельзя было подать кортеж в функцию, принимающую кортеж.
Re[3]: Inline records
От: thesz Россия http://thesz.livejournal.com
Дата: 11.10.09 08:16
Оценка:
Здравствуйте, VladD2, Вы писали:

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


T>>Ты бы для разнообразия ознакомился бы с состоянием дел в других языках, что ли.


VD>Для разнообразия ознакомился.

VD>А ты бы прежде чем вепендриваться с начало бы разобрался в сути вопроса.

Уверяю тебя, я разбираюсь в сути вопроса.

Тем более, что для зависимых типов твои рассуждения про простых смертных ещё менее важны.

T>>А то начал за здравие — кортежи в функциональных языках, — а закончил за упокой — в Nemerle так, про другие не знаю.


VD>Ну и за каким фигом ты эту ссылку дал? Тему то читал?


Затем, что в Хаскеле неверно "Кортеж — это часть дизайна языка." Там верно "кортеж — это удобное сокращение алгебраического типа данных с одним конструктором." Демонстрацией чего и была та ссылка.

Так что твои рассуждения основаны на выборке из одного отсчёта, а потому неверны.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[3]: Inline records
От: hi_octane Беларусь  
Дата: 11.10.09 10:46
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Так вот анонимные типы — это недоразумение возникшее в следствии того, что в МС вместо грамотного проектирования всей системы (CLR и языков) решали частную проблему встраивания запросов в язык. Проблема этого неполноценного решения заключается в том, что в дотнет нет структурной эквивалентности, а без него тип (именно полноценный тип, а не синтаксический сахар, что бы встроен в шарп) создать нельзя.


А так ли уж нет возможности вручную эмулировать структурную эквивалентность туплов хотя-бы для Nemerle? Если туплы с именованными свойствами будут наследниками туплов со свойствами безымянными + куча приседаний макросами и в тех и в других что-бы реализовать Equals, все возможные Comparable и приведения для лёгкой конверсии из одного тупла в другой — может прокатит хотя-бы в рамках одной сборки?
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.10.09 13:40
Оценка: -1
Здравствуйте, thesz, Вы писали:

T>Уверяю тебя, я разбираюсь в сути вопроса.


T>Тем более, что для зависимых типов твои рассуждения про простых смертных ещё менее важны.


Ты не здоров? А может ты не трезв? Такое ощущение что нет. Где здесь хоть слово о ЗТ?

T>Затем, что в Хаскеле неверно "Кортеж — это часть дизайна языка." Там верно "кортеж — это удобное сокращение алгебраического типа данных с одним конструктором." Демонстрацией чего и была та ссылка.


Ну, и что? Речь то совершенно не о том. Иди под учи C# тогда с тобой о чем-то можно будет говорить. А пока ты заперт в своем мире хаскеля, ты такой же блаб, как те кто знаком только с миром C#.

T>Так что твои рассуждения основаны на выборке из одного отсчёта, а потому неверны.


Очень советую тебе разобраться в сути вопроса. Разберешься приходи, поговорим. А пока, пока.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.10.09 13:45
Оценка:
Здравствуйте, hi_octane, Вы писали:

VD>>Так вот анонимные типы — это недоразумение возникшее в следствии того, что в МС вместо грамотного проектирования всей системы (CLR и языков) решали частную проблему встраивания запросов в язык. Проблема этого неполноценного решения заключается в том, что в дотнет нет структурной эквивалентности, а без него тип (именно полноценный тип, а не синтаксический сахар, что бы встроен в шарп) создать нельзя.


_>А так ли уж нет возможности вручную эмулировать структурную эквивалентность туплов хотя-бы для Nemerle?


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

_>Если туплы с именованными свойствами будут наследниками туплов со свойствами безымянными + куча приседаний макросами и в тех и в других что-бы реализовать Equals, все возможные Comparable и приведения для лёгкой конверсии из одного тупла в другой — может прокатит хотя-бы в рамках одной сборки?


В рамках одной сборки вообще проблем нет.

Если оставить базовым типом кортеж, а поля "приклеить" на уровне системы типов с аннотациями в виде атрибутов, то один язык сможет их контролировать даже за рамками сборки. Но это будет чисто текстуальный контроль.

Кстати, для кортежей это уже в некотором роде есть. Кортежи уже обрабатываются особым образом. Например, в них них есть поля, но в немерле эти поля не видны. Вместо этого используется синтаксис доступа по индексу.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.10.09 13:47
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>

VD>>def x = f();
VD>>... = x.var1 + x.var2;
VD>>... x.var3


L>Под несовместимостью я имел в виду следующее: если есть функция g, которая отличается от f только именем возвращаемого параметра, то она не может быть использована в приведенном куске кода.


В смысле у g и f возвращают несколько параметров у которых одинакова структура, но разные имена?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.10.09 13:54
Оценка:
Здравствуйте, FR, Вы писали:

FR>Кроме того структуры и классы имеют свойство .tupleof и можно их одним вызовом отправлять в параметры функции при совпадении типов конечно:...


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

Кстати, а как при этом дело обстоит с инкапсуляцией? У структур в Ди могут быть не публичные поля?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Inline records
От: Lloyd Россия  
Дата: 11.10.09 14:34
Оценка:
Здравствуйте, VladD2, Вы писали:

L>>Под несовместимостью я имел в виду следующее: если есть функция g, которая отличается от f только именем возвращаемого параметра, то она не может быть использована в приведенном куске кода.


VD>В смысле у g и f возвращают несколько параметров у которых одинакова структура, но разные имена?


У возвращаемых параметров — типы одинаковые, но различаются имена.
Re[5]: Inline records
От: FR  
Дата: 11.10.09 15:04
Оценка:
Здравствуйте, VladD2, Вы писали:

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


На D тоже можно написать (сначала и был вроде шаблонный вариант), но встроенный удобней.

VD>Кстати, а как при этом дело обстоит с инкапсуляцией? У структур в Ди могут быть не публичные поля?


Могут, для структур в кортеж преобразуются все поля независимо от публичности. Вот для классов преобразуются только публичные поля.
Re: Inline records
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.10.09 17:15
Оценка:
Здравствуйте, VladD2, Вы писали:

Ну, собственно, я тебе давно уже говорил, что на некоторых задачах такое очень сильно упростило бы жизнь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1245 on Windows 7 6.1.7600.0>>
AVK Blog
Re[5]: Inline records
От: thesz Россия http://thesz.livejournal.com
Дата: 12.10.09 09:03
Оценка:
Здравствуйте, VladD2, Вы писали:

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


T>>Уверяю тебя, я разбираюсь в сути вопроса.


T>>Тем более, что для зависимых типов твои рассуждения про простых смертных ещё менее важны.

VD>Ты не здоров? А может ты не трезв? Такое ощущение что нет. Где здесь хоть слово о ЗТ?

Видишь ли, эти самые безымянные записи с именованными полями нужны ровно для того, чтобы не перепутать startTime и endTime. Поддержать инвариант startTime <= endTime. Сделать их "типы" "зависящими" от их значений. Зависимыми, иными словами.

Если смотреть на это с такой точки зрения, то всё это обсуждение яйца выеденного не стоит.

T>>Затем, что в Хаскеле неверно "Кортеж — это часть дизайна языка." Там верно "кортеж — это удобное сокращение алгебраического типа данных с одним конструктором." Демонстрацией чего и была та ссылка.

VD>Ну, и что? Речь то совершенно не о том. Иди под учи C# тогда с тобой о чем-то можно будет говорить. А пока ты заперт в своем мире хаскеля, ты такой же блаб, как те кто знаком только с миром C#.

В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

Поэтому выгодней учить Хаскель. Что я и делаю.

T>>Так что твои рассуждения основаны на выборке из одного отсчёта, а потому неверны.

VD>Очень советую тебе разобраться в сути вопроса. Разберешься приходи, поговорим. А пока, пока.

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

Но ты спрашивай, я объясню.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[6]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.10.09 11:03
Оценка:
Здравствуйте, thesz, Вы писали:

T>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

T>Поэтому выгодней учить Хаскель. Что я и делаю.

В C# попадают идеи и из других языков, поэтому выгоднее учить C#, особенно в долгосрочной песпективе
Re[7]: Inline records
От: thesz Россия http://thesz.livejournal.com
Дата: 12.10.09 11:11
Оценка: +1 :))) :))
Здравствуйте, gandjustas, Вы писали:

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


T>>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

T>>Поэтому выгодней учить Хаскель. Что я и делаю.
G>В C# попадают идеи и из других языков, поэтому выгоднее учить C#, особенно в долгосрочной песпективе

Да. Но в другие языки эти идеи попадают из того же Хаскеля.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[8]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.10.09 12:09
Оценка: :)
Здравствуйте, thesz, Вы писали:

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


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


T>>>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

T>>>Поэтому выгодней учить Хаскель. Что я и делаю.
G>>В C# попадают идеи и из других языков, поэтому выгоднее учить C#, особенно в долгосрочной песпективе

T>Да. Но в другие языки эти идеи попадают из того же Хаскеля.


Динамическая типизация (в C# 4) и метапрограммирование (возможно в C# 5+) в хаскеле есть?

А если нет, то таки выгоднее учить C#
Re[9]: Inline records
От: thesz Россия http://thesz.livejournal.com
Дата: 12.10.09 12:40
Оценка:
Здравствуйте, gandjustas, Вы писали:

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


T>>>>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

T>>>>Поэтому выгодней учить Хаскель. Что я и делаю.
G>>>В C# попадают идеи и из других языков, поэтому выгоднее учить C#, особенно в долгосрочной песпективе
T>>Да. Но в другие языки эти идеи попадают из того же Хаскеля.
G>Динамическая типизация (в C# 4) и метапрограммирование (возможно в C# 5+) в хаскеле есть?

Да (2003-й, что ли, год, а то и ещё раньше) и Да (2005-й, что ли, год, а то и ещё раньше).

G>А если нет, то таки выгоднее учить C#


Нет, не выгодней.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[6]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.10.09 13:19
Оценка: +1
Здравствуйте, thesz, Вы писали:

T>Видишь ли, эти самые безымянные записи с именованными полями нужны ровно для того, чтобы не перепутать startTime и endTime. Поддержать инвариант startTime <= endTime. Сделать их "типы" "зависящими" от их значений. Зависимыми, иными словами.


Инвариантом здесь и не пахнет. Не надо домысливать.
Все это нужно чтобы помочь человеку. В первую очередь — это возможность доступа к полям по имени. Скажем, если у нас есть некий запрос возвращающий список кортежей, то куда удобнее обращаться к полям кортежей по именам, а не через позиции.

T>Если смотреть на это с такой точки зрения, то всё это обсуждение яйца выеденного не стоит.


Не очень понятно с чего бы это, но на то они и разные точки зрения.

T>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.


Возможно тебя это рассторит, но подовляющему большинству программистов плевать на хаскель и на то что в него попадает.
И плевать, на то что ты не можешь пройти мимо любой темы, посвященной дизайну ЯП, где забыли упомянуть Наскель.

T>Поэтому выгодней учить Хаскель. Что я и делаю.


Ну, учи. Что к другим то приставать? Мне Hasskel без надобнсоти. Что на нем можно сделать? Чистые вычисления меня не интересуют. От иероглифической записи меня не прем (иначе я наверно перся бы от K или J). Мне нужен инструмент позволяющий решать проблемы реального мира. На сегодня по сочетанию факторов (языковые возможности, расширяемость, поддержка ДСЛ-ей, возможность использования огромных мэйнстрим-библиотек) для меня таковым языком является Nemerle. Для подавляющего большинства людей — это, вообще, C# и Java.

В шарпе и немерле есть определенная проблема. Вот ее я и хотел обсудить в этой теме.

T>Вся беда в том, что я разбираюсь на более высоком уровне, чем ты. Вот ты и не можешь понять.


Понуешся ты, несомненно, на более высоком уровне. Остальное сильно сомнительно.

T>Но ты спрашивай, я объясню.


Мне тебя не о чем спрашивать. Твоя позиция почти всегда деструктривно. Потому с тобой просто не интересно разговаривать. Вместо высказывания интересный мыслей ты постоянно выёживашся и потнушся.
Научись воспринимать окружающих как равных. Объяснять свою точку без понтов, воспринимать чужую точку зрения. Тогда с тобой будет интересно вести беседы. А так одно раздражения от общения с тобой получаешь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 12.10.09 13:27
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Динамическая типизация (в C# 4) и метапрограммирование (возможно в C# 5+) в хаскеле есть?


В Haskell, как в Греции — есть всё
Re[7]: Inline records
От: thesz Россия http://thesz.livejournal.com
Дата: 12.10.09 13:40
Оценка:
Здравствуйте, VladD2, Вы писали:

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


T>>Видишь ли, эти самые безымянные записи с именованными полями нужны ровно для того, чтобы не перепутать startTime и endTime. Поддержать инвариант startTime <= endTime. Сделать их "типы" "зависящими" от их значений. Зависимыми, иными словами.

VD>Инвариантом здесь и не пахнет. Не надо домысливать.
VD>Все это нужно чтобы помочь человеку. В первую очередь — это возможность доступа к полям по имени. Скажем, если у нас есть некий запрос возвращающий список кортежей, то куда удобнее обращаться к полям кортежей по именам, а не через позиции.

Или по сравнению с образцом.

T>>Если смотреть на это с такой точки зрения, то всё это обсуждение яйца выеденного не стоит.

VD>Не очень понятно с чего бы это, но на то они и разные точки зрения.

Всё, что ты делаешь (и я делаю, и кто-либо ещё делает в районе проектирования любых ЯП), направлено на уменьшение количества ошибок, допускаемых программистами.

Это единственная разумная точка зрения. "Мы все уменьшаем количество ошибок наших пользователей".

Далее идёт оценка размена — какие ошибки мы уменьшаем, какие увеличиваем. Где мы потратим больше времени, где меньше — второй вариант оценки.

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

А вот наличие имён для индикации инвариантов уже более интересно. Про наличие инвариантов и говорить отдельно не стоит, и так понятно, что это круто.

T>>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

VD>Возможно тебя это рассторит, но подовляющему большинству программистов плевать на хаскель и на то что в него попадает.
VD>И плевать, на то что ты не можешь пройти мимо любой темы, посвященной дизайну ЯП, где забыли упомянуть Наскель.

Я так думаю, что это их проблемы, не мои.

Это они не знакомы с Хаскелем и потому, начиная говорить о функциональных языках программирования, сворачивают на то, что им известно.

T>>Поэтому выгодней учить Хаскель. Что я и делаю.

VD>Ну, учи. Что к другим то приставать?

Я альтруист.

VD>Мне Hasskel без надобнсоти. Что на нем можно сделать? Чистые вычисления меня не интересуют.


А зря.

VD>От иероглифической записи меня не прем (иначе я наверно перся бы от K или J). Мне нужен инструмент позволяющий решать проблемы реального мира.


Это любой ЯП общего назначения.

Интересно, ты не упомянул "наиболее эффективным образом".

T>>Но ты спрашивай, я объясню.

VD>Мне тебя не о чем спрашивать. Твоя позиция почти всегда деструктривно. Потому с тобой просто не интересно разговаривать. Вместо высказывания интересный мыслей ты постоянно выёживашся и потнушся.
VD>Научись воспринимать окружающих как равных. Объяснять свою точку без понтов, воспринимать чужую точку зрения. Тогда с тобой будет интересно вести беседы. А так одно раздражения от общения с тобой получаешь.

Ух ты!

Это же комплимент.

Поскольку я слышу это от человека, которому последние два параграфа говорят гораздо чаще, чем мне.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[7]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 12.10.09 13:42
Оценка: 1 (1) +1
Здравствуйте, VladD2, Вы писали:

VD>Все это нужно чтобы помочь человеку. В первую очередь — это возможность доступа к полям по имени. Скажем, если у нас есть некий запрос возвращающий список кортежей, то куда удобнее обращаться к полям кортежей по именам, а не через позиции.


А я вот не понял (это безотносительно вашей дискуссии), зачем там имена. Ведь таплы используются только в случаях, когда эти имена не нужны — там всегда case analysis. А следовательно, в коде эти имена будут присутствовать в разборе. Т.е. мы будем часто писать

(left, right) = getBranches(tree)
doSmthgWith(left, right)


а не

branches = getBranches(tree)
doSmthgWith(branches.left, branches.right)


Я вообще не вижу, когда второе может быть выгоднее. light records всё таки используются немного для других целей.
Re: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 06:23
Оценка: 8 (1) -1
Здравствуйте, VladD2, Вы писали:

VD>А почему бы не рассматривать возвращаемое значение функции так же как набор параметров? При этом синтаксис может быть совершенно разрообразным. Например, мы можем объявлять функцию так:


<skipped>

Возвращаемое значение функции — это просто соглашение языка о том, что один из выходных параметров передается как результат.

Гипотетический язык

(string ret1, int ret2) FuncName(string param1, int param2)
{
}

(string ret1) FuncName(string param1, int param2, out int ret2)
{
}

(int ret2) FuncName(string param1, int param2, out string ret1)
{
}

void FuncName(string param1, int param2, out int ret2, out string ret1)
{
}

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

string s = "Func Name is " + FuncName(аргументы);

а остальные не позволят. Или предпочесть вариант 3, чтобы можно было писать

int t = FuncName(аргументы);

если мне важнее именно возвращаемое целое.
With best regards
Pavel Dvorkin
Re[3]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.10.09 06:27
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>А "с полной совместимостью по типу, невзирая на имена свойств" называется структурной эквивалентностью. Она тоже есть почти во всех языках, даже в С++ (но не в Яве и Шарпе).

VD>Так вот для анонимных типов структурная эквивалентность не поддерживается. Причем это проблема CLR.
VD>Я же предлагаю не объявление нового типа, как кому-то тут показалось, а аннотацию кортежа. Мы же не говорим, что параметры функции — это анонимный тип только на основании того, что у них есть имена? Вот тут та же фигня. Имена всего лишь дополнительная аннотация не вводящая новый тип, но повышающая удобство на практике.

С какого-то количества элементов в ответе таки да (опыт того же Erlang). Просто примеры не совсем адекватны: пока этих элементов 2-3 — собеседники не видят смысла в подобной "хитрой штуке". На практике же дофига случаев, когда их и 10, и 30. Если их состав определён на этапе компиляции, то рисовать мап на них — нехорошо, лучше пользоваться именованными статическими позициями. По сказанному, я "издалека" поддерживаю эту идею.

S>>Т.е. если мы оставляем запись string*int для обозначения кортежа, то функция типа string*int->string*int должна как принимать именно кортеж, так и возвращать его.


VD>Она так и делает. Вот так записывается конструктор кортежа:

VD>
("aaa", 12)

VD>а вот так вызов функции:
VD>
f("aaa", 12)

VD>найдите 10 различий.

Полной эта схема будет тогда, когда можно будет где-то отдельно сформировать набор аргументов функции в виде кортежа и затем передать его в функцию. Аналоги — питоновый callable(*args) и эрланговый apply(Fun,Args) (в эрланге передаётся список, а не кортеж, но это тут менее важно). Не знаю, как оно сейчас в Nemerle, но описанного тут — явно недостаточно.

(К слову — тут явно не хватает ещё одного вида скобок для кортежей. Для сравнения, в Erlang — {...}, а в таком оригинальном языке как Рапира (на формирование которого явно повлияли какие-то ранние ФЯ) — <*...*>. В питоне обошлись круглыми скобками и поэтому для кортежа в один элемент используют синтаксический костыль)
The God is real, unless declared integer.
Re[2]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 07:15
Оценка: 12 (3) +3 :)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>в сущности эквивалентны.

Дьявол, как всегда — в деталях.
Для дальнейшего я буду использовать шарп, но аналогичные грабли есть и в твоих любимых плюсах.
Сравним следующие две строки:
int a, b;
a = GetSomeInt();
GetSomeInt(out b); // в плюсах был бы соответственно &b

Одинакова ли семантика первой и второй строчки? Нет, неодинакова. а получает значение ровно один раз, а в b происходит неопределённое количество присваиваний.
Это может играть важную роль в тех случаях, когда аргумент в процессе действия GetSomeInt изменяется еще где-то.
И, как следствие, мы имеем разные ограничения на безопасность типов. Для a мы можем применить любой тип, совместимый по присваиванию с int, например — double.
Для b — только сам int. Потому что иначе неясна семантика его использования внутри функции.

Как это относится к кортежам? Да точно так же.
Запись вида
(int a, string b) = GetSomePair();

не может быть эквивалентна записи
GetSomePair(out a, out b);


Просто потому, что иначе вот такие две записи не будут себя вести эквивалентно:
int a = GetSomeInt();
(int a) = GetSomeInt();

А это уже явное противоречие здравому смыслу, с далеко идущими последствиями.

Поэтому в языке нужно будет давать чёткий способ отличать возврат структурированного значения (return (5, "hello") от присваивания out-параметрам.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 07:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Для дальнейшего я буду использовать шарп, но аналогичные грабли есть и в твоих любимых плюсах.

S>Сравним следующие две строки:
S>
S>int a, b;
S>a = GetSomeInt();
S>GetSomeInt(out b); // в плюсах был бы соответственно &b
S>

S>Одинакова ли семантика первой и второй строчки? Нет, неодинакова. а получает значение ровно один раз, а в b происходит неопределённое количество присваиваний.

Расшифруй , пожалуйста, фразу, ввиду того, что она просто непонятна сама по себе. Что значит "в b происходит неопределённое количество присваиваний" ? В b вообще ничего не может происходить, это не код. Или ты хочешь сказать, что b здесь присваивание происходит несколько раз ? Тогда почему ?

void Func(char& p)
{
p = 'a';
}

char p;
Func(p);

Где второе присваивание p ? При вызове передается адрес, а не значение. Внутри присваивание. Больше ничего.

S>Это может играть важную роль в тех случаях, когда аргумент в процессе действия GetSomeInt изменяется еще где-то.


А если а во время a = GetSomeInt(); тоже где-то меняется ? В чем разница ? Кстати, как это они меняются в однопоточном приложении или кто им позволил без синхронизации меняться в многопоточном ?

S>И, как следствие, мы имеем разные ограничения на безопасность типов. Для a мы можем применить любой тип, совместимый по присваиванию с int, например — double.


А это уже просто специфика операции присваивания, которая прямого отношения к вызову функции не имеет . Вызов функции сам по себе, а дальше присваивание, а там правила совместимости типов

float f;
int i;

f = i; // так же можно писать ?
f = SomeIntFunc(i); // ну и так можно по той же причине
With best regards
Pavel Dvorkin
Re[4]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.10.09 08:12
Оценка: +1 -1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Расшифруй , пожалуйста, фразу, ввиду того, что она просто непонятна сама по себе. Что значит "в b происходит неопределённое количество присваиваний" ? В b вообще ничего не может происходить, это не код. Или ты хочешь сказать, что b здесь присваивание происходит несколько раз ? Тогда почему ?


PD>void Func(char& p)

PD>{
PD> p = 'a';
PD>}

PD>char p;

PD>Func(p);

PD>Где второе присваивание p ? При вызове передается адрес, а не значение. Внутри присваивание. Больше ничего.


Даже если одно, то функция может наследить в том числе сразу за p, или перед p. А в случае char p = Func(), это не грозит.

PD>А если а во время a = GetSomeInt(); тоже где-то меняется ? В чем разница ? Кстати, как это они меняются в однопоточном приложении или кто им позволил без синхронизации меняться в многопоточном ?

Где написано что приложение однопоточное? Да и речь идет о семантике вызова, а не о том, есть ли там синхронизация. Семантика разная, в этом Sinclair прав!
Re[4]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 08:25
Оценка: +2
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>Где второе присваивание p ?

Неужто сам не догадываешься?
Ок, показываю (следи за руками):
void Func(int &sum, int start, int end)
{
  for(int i=start; i<end; i++)
    sum*= i;
}

Сколько раз присваивается sum? Заметь, кстати, что компилятор C#, в отличие от C++, отличает ref от out — для последнего он требует определяющего присванивания. Т.е. мы не знаем точно, сколько раз присваивается sum, но знаем, что не меньше одного:
void Func(out int sum, int start, int end)
{
  sum=start; // тут опять тонкости definite assignment, связанные с out - компилятор не даёт программисту полагаться на начальное значение этого параметра
  for(int i=start; i<end; i++)
    sum*= i;
}


PD>А если а во время a = GetSomeInt(); тоже где-то меняется ? В чем разница ?

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

PD>Кстати, как это они меняются в однопоточном приложении или кто им позволил без синхронизации меняться в многопоточном?

Элементарно они меняются. Допустим, эта функция вызывает callback. А тело callback уже запросто в том же потоке меняет a непредсказуемым образом.

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

PD>А это уже просто специфика операции присваивания, которая прямого отношения к вызову функции не имеет . Вызов функции сам по себе, а дальше присваивание, а там правила совместимости типов


PD>float f;

PD>int i;

PD>f = i; // так же можно писать ?

PD>f = SomeIntFunc(i); // ну и так можно по той же причине
в том то и дело. Писать f = SomeIntFunc(i) — можно. А вот так — тебе не даст компилятор (насколько я помню C++):
SomeIntFunc(i, &f)

При условии, что функция определена вот таким образом:
void SomeIntFunc(int a, int &b)
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 08:26
Оценка: :)
Здравствуйте, samius, Вы писали:


S>Даже если одно, то функция может наследить в том числе сразу за p, или перед p. А в случае char p = Func(), это не грозит.


Можно чуть точнее ? Перед p — это где, до вызова ? До этого мне дела нет, меня не интересует, чему равен p до вызова вообще. После вызова ? Поезд ушел, пишите письма.

PD>>А если а во время a = GetSomeInt(); тоже где-то меняется ? В чем разница ? Кстати, как это они меняются в однопоточном приложении или кто им позволил без синхронизации меняться в многопоточном ?

S>Где написано что приложение однопоточное? Да и речь идет о семантике вызова, а не о том, есть ли там синхронизация. Семантика разная, в этом Sinclair прав!

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

(int a, float b) F()

мне может быть полезнее чем

void F(int&a, float&b)
With best regards
Pavel Dvorkin
Re[6]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.10.09 08:57
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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



S>>Даже если одно, то функция может наследить в том числе сразу за p, или перед p. А в случае char p = Func(), это не грозит.


PD>Можно чуть точнее ? Перед p — это где, до вызова ? До этого мне дела нет, меня не интересует, чему равен p до вызова вообще. После вызова ? Поезд ушел, пишите письма.

Перед p — это по отрицательному смещению относительно адреса p. Но ключевое тут может курсивом. В здравом уме никто так делать не станет, но с другой стороны, никто и не запретит.

PD>Семантика , может, немного и отличается, но меня интересуют исключительно практические различия, а их практически нет . Проще говоря, я не вижу чем


PD>(int a, float b) F()


PD>мне может быть полезнее чем


PD>void F(int&a, float&b)


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

Если программист по сути одиночка, то он сам расставляет мины, сам их и обходит и ни на что не жалуется. Если в команде присутствуют люди, которые не способны постичь далеко идущий высший замысел, то это обычно и приносит проблемы.
Re[7]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 09:07
Оценка:
Здравствуйте, samius, Вы писали:

PD>>Можно чуть точнее ? Перед p — это где, до вызова ? До этого мне дела нет, меня не интересует, чему равен p до вызова вообще. После вызова ? Поезд ушел, пишите письма.

S>Перед p — это по отрицательному смещению относительно адреса p.

А мне какое дело до этого ? Если при этом p не перекрывается — на здоровье. Если перекрывается — нечего ерундой заниматься.

>Но ключевое тут может курсивом. В здравом уме никто так делать не станет, но с другой стороны, никто и не запретит.


Не запретит. Ерундой заниматься никто не запрещает, но это многими способами можно сделать


PD>>(int a, float b) F()


PD>>мне может быть полезнее чем


PD>>void F(int&a, float&b)


S>Вызвав такой метод очень легко что-нибудь где-нибудь испортить, особенно если переменные лежат не на стеке.


Какой ? Второй ? Ничего тут испортить нельзя. Если, конечно, не брать там указатели и не гонять их как попало. Но так можно везде испортить.
With best regards
Pavel Dvorkin
Re[8]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.10.09 09:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


>>Но ключевое тут может курсивом. В здравом уме никто так делать не станет, но с другой стороны, никто и не запретит.


PD>Не запретит. Ерундой заниматься никто не запрещает, но это многими способами можно сделать


PD>>>(int a, float b) F()


PD>>>мне может быть полезнее чем


PD>>>void F(int&a, float&b)


S>>Вызвав такой метод очень легко что-нибудь где-нибудь испортить, особенно если переменные лежат не на стеке.


PD>Какой ? Второй ? Ничего тут испортить нельзя. Если, конечно, не брать там указатели и не гонять их как попало. Но так можно везде испортить.


Ну а первый способ таки исключает способы отрубить шашкой большой палец ноги (хоть и не себе), а следовательно полезнее.
Re[5]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 09:19
Оценка:
Здравствуйте, Sinclair, Вы писали:

В основном ты прав, но ИМХО все это эффекты второго порядка. Я согласен, что писать придется несколько иначе и эти эффекты надо учитывать. Но , на мой взгляд, эти отличия второго порядка недостаточны, чтобы вводить новое понятие в язык, поскольку пусть не на 100%, но на 99.9% мы уже имеем то же самое иным способом.
With best regards
Pavel Dvorkin
Re[9]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 09:48
Оценка:
Здравствуйте, samius, Вы писали:


PD>>Какой ? Второй ? Ничего тут испортить нельзя. Если, конечно, не брать там указатели и не гонять их как попало. Но так можно везде испортить.


S>Ну а первый способ таки исключает способы отрубить шашкой большой палец ноги (хоть и не себе), а следовательно полезнее.


Меня, честно сказать, очень мало интересуют все эти проблемы "как бы чего не испортить". Не хотите испортить — пишите аккуратно и не портите. Если мне некие средства предложат, позволяющие сделать то. что я делаю сейчас, более элегантным способом при не худшей эффективности, я буду это изучать и использовать. Если же речь идет о синтаксических изысках, тотальной защите от всевозможных угроз или же неэффектиных, пусть и красивых решениях — это мне не интересно.
With best regards
Pavel Dvorkin
Re[6]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 09:53
Оценка: +4
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>В основном ты прав, но ИМХО все это эффекты второго порядка. Я согласен, что писать придется несколько иначе и эти эффекты надо учитывать.
Ты, похоже, так и не понял, о чём я говорю.
Я говорю, что "несколько иначе" делает фичу "возврат множества значений через &-аргументы" необоснованно неудобной. "Эффекты", о которых я написал, должен учитывать не программист, а язык программирования. И язык таки это делает — я написал, как именно.

Но учёт этих эффектов второго порядка компилятором вынуждает его накладывать на код ограничения уже первого порядка.
Простейшая штука — результат функции, которая возвращает "целую 2d-точку", не получается присвоить "плавающей 2d-точке". В отличие от простого случая возврата единственного аргумента.
Я так думаю, что если бы в С++ запретили возвращать значения вообще, и потребовали всегда в таких случаях передавать результат через &-аргументы, то ты бы быстро оценил этот "второй порядок".
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.10.09 09:55
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Меня, честно сказать, очень мало интересуют все эти проблемы "как бы чего не испортить". Не хотите испортить — пишите аккуратно и не портите. Если мне некие средства предложат, позволяющие сделать то. что я делаю сейчас, более элегантным способом при не худшей эффективности, я буду это изучать и использовать. Если же речь идет о синтаксических изысках, тотальной защите от всевозможных угроз или же неэффектиных, пусть и красивых решениях — это мне не интересно.


А здесь речь как раз не об эффективности (исполнения кода), а как раз о том, что бы не испортить, не перепутать, не рисовать крестики чтобы не забыть, и т.п. Об удобвстве и безопасности, в основном.
Re[7]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.10.09 09:57
Оценка: +1 :)
Здравствуйте, Sinclair, Вы писали:

S>Я так думаю, что если бы в С++ запретили возвращать значения вообще, и потребовали всегда в таких случаях передавать результат через &-аргументы, то ты бы быстро оценил этот "второй порядок".

Ага, и еще после каждого вызова проверять HRESULT! Что-то это мне напоминает
Re[11]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 10:45
Оценка: -1
Здравствуйте, samius, Вы писали:

S>А здесь речь как раз не об эффективности (исполнения кода), а как раз о том, что бы не испортить, не перепутать, не рисовать крестики чтобы не забыть, и т.п. Об удобвстве и безопасности, в основном.


Ну я и говорю, что ИМХО эти игры не стоят того, чтобы ради них менять серьезно язык.
With best regards
Pavel Dvorkin
Re[12]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.10.09 10:55
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Ну я и говорю, что ИМХО эти игры не стоят того, чтобы ради них менять серьезно язык.


На это возразить нечего, остается только принять к сведению.
Re[7]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 10:56
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:

PD>>В основном ты прав, но ИМХО все это эффекты второго порядка. Я согласен, что писать придется несколько иначе и эти эффекты надо учитывать.
S>Ты, похоже, так и не понял, о чём я говорю.
S>Я говорю, что "несколько иначе" делает фичу "возврат множества значений через &-аргументы" необоснованно неудобной. "Эффекты", о которых я написал, должен учитывать не программист, а язык программирования. И язык таки это делает — я написал, как именно.

Да все я понял, просто я считаю эти эффекты маловажными.

S>Но учёт этих эффектов второго порядка компилятором вынуждает его накладывать на код ограничения уже первого порядка.

S>Простейшая штука — результат функции, которая возвращает "целую 2d-точку", не получается присвоить "плавающей 2d-точке". В отличие от простого случая возврата единственного аргумента.

Присвоить все равно нельзя. Можно лишь получить результат как целую точку, а потом через конструктор получить плавающую. В любом случае будет 2 объекта.

S>Я так думаю, что если бы в С++ запретили возвращать значения вообще, и потребовали всегда в таких случаях передавать результат через &-аргументы, то ты бы быстро оценил этот "второй порядок".


Ну и что ?


class IPoint {
public :
    int x, y;
};

class FPoint {
public :
    float x, y;
    FPoint(IPoint& ip)
    {
        x = ip.x;
        y = ip.y;
    }
};
IPoint GetPoint()
{
    IPoint p;
    p.x = p.y = 0;
    return p;
}

void GetPointByRef(IPoint& ip)
{
    ip.x = ip.y = 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
    FPoint fp = GetPoint();

    IPoint ip;
    GetPointByRef(ip);
    FPoint fp1 = ip;
}




Принципиальной разницы не вижу.
With best regards
Pavel Dvorkin
Re[8]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 11:37
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:
S>>Простейшая штука — результат функции, которая возвращает "целую 2d-точку", не получается присвоить "плавающей 2d-точке". В отличие от простого случая возврата единственного аргумента.
PD>Присвоить все равно нельзя. Можно лишь получить результат как целую точку, а потом через конструктор получить плавающую. В любом случае будет 2 объекта.
Что значит "всё равно"? Еще раз привожу примитивный пример:
double f = GetSomeInt(); // работает
(double f) = GetSomeInt(); // не работает 
// мы помним, что строчка выше - всего лишь извращённый плод моего сознания, алиас для 
GetSomeInt(&f); // не работает, потому что &double не приводится к &int

Меня не интересует, что там происходит "за кадром". Код расширения инта до флоата подставлен компилятором. Если я поменяю там float на double, или, упаси байт, на какой-нибудь extended, то мне не придётся переписывать код. (Под "там" я имею в виду декларацию f — а она может случиться в совершенно другом месте. Например, это поле какой-то структуры, которой я параметризовал шаблонную функцию, в которой сделан вызов GetSomeInt()). И я всё еще имею гарантии статической проверки совместимости типов.

И вся эта могучая магия, надёжно отлаженная в поколениях компиляторов, мгновенно испаряется в тот момент, когда я хочу получить два значения вместо одного.

А вот более реалистичный пример
(int modulo, int reminder) = IntDivide(arg, divisor); 
// не работает, если arg и divisor - byte; 
// при очевидном определении IntDivide как 
template<classname T>
void IntDivide(T arg, T divisor, T &modulo, T &remainder)
// вместо этого мне нужно вручную выписывать нудные промежуточные присваивания
// хотя вот такой код будет работать безо всяких проблем:
int modulo = arg / divisor; // здесь сработает очевидное расширение байта до инта
int remainder = arg % divisor; // но делить придётся дважды, вместо быстрой операции, которая получает сразу оба числа.

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

PD>Ну и что ?


PD>Принципиальной разницы не вижу.

Ну да. Только я хочу иметь возможность всю эту кунсткамеру ликвидировать движением брови — то, на что я хочу тратить одну строчку, ты предлагаешь выписывать в целую страницу. На которой, кстати, ты сделал две ошибки (возможно, намеренных), и при этом всё еще не поддерживаешь IPoint<double>.
Понятно, что мой пример с modulo и remainder решается путём введения именованного класса DivisionResult, с соответствующими конструкторами и т.п — аналогично твоему примеру.
Но это всё — мучительный boilerplate. Хочется-то получить всё то же самое забесплатно, а не так, чтобы приходилось всё-всё писать руками.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Inline records
От: Трурль  
Дата: 13.10.09 11:49
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


S>Для дальнейшего я буду использовать шарп, но аналогичные грабли есть и в твоих любимых плюсах.

S>Сравним следующие две строки:
S>
S>int a, b;
S>a = GetSomeInt();
S>GetSomeInt(out b); // в плюсах был бы соответственно &b
S>

S>Одинакова ли семантика первой и второй строчки? Нет, неодинакова. а получает значение ровно один раз, а в b происходит неопределённое количество присваиваний.

В Аде в подобном случае наблюдается полная эквивалентность.
a, b : Integer;
a = GetSomeInt();
GetSomeInt(b);
Re[9]: Inline records
От: Pavel Dvorkin Россия  
Дата: 13.10.09 11:57
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Меня не интересует, что там происходит "за кадром"


А меня именно это интересует, а все эти синтаксические тонкости — не очень. Я свою точку зрения подробно объяснил вот здесь

http://rsdn.ru/forum/philosophy/3566935.1.aspx
Автор: Pavel Dvorkin
Дата: 13.10.09


И ИМХО не надо вводить в язык дополнительные сущности, пока не станет ясно, что без них обходиться нельзя. Именно нельзя. А не "мне хотелось бы это написать в одну строку, а не 3"


PD>>Ну и что ?


PD>>Принципиальной разницы не вижу.

S>Ну да. Только я хочу иметь возможность всю эту кунсткамеру ликвидировать движением брови — то, на что я хочу тратить одну строчку, ты предлагаешь выписывать в целую страницу.

Меня мало интересует количество строк.

>На которой, кстати, ты сделал две ошибки (возможно, намеренных), и при этом всё еще не поддерживаешь IPoint<double>.


Я привел минимальный демонстрационный пример, который показывает тебе суть того, о чем я говорю, и я вовсе не собирался в нем писать больше, чем мне нужно для этого примера.

S>Но это всё — мучительный boilerplate. Хочется-то получить всё то же самое забесплатно, а не так, чтобы приходилось всё-всё писать руками.


Бесплатный сыр бывает только в мышеловках
With best regards
Pavel Dvorkin
Re[4]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 12:12
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>В Аде в подобном случае наблюдается полная эквивалентность.

Т>
Т>a, b : Integer;
Т>a = GetSomeInt();
Т>GetSomeInt(b); 
Т>

А там есть definite assignment? Какие ограничения на использование b накладываются на тело GetSomeInt?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 12:46
Оценка: +2
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А меня именно это интересует, а все эти синтаксические тонкости — не очень. Я свою точку зрения подробно объяснил вот здесь

Это не синтаксис, а семантика.

PD>И ИМХО не надо вводить в язык дополнительные сущности, пока не станет ясно, что без них обходиться нельзя. Именно нельзя. А не "мне хотелось бы это написать в одну строку, а не 3"

И каков критерий "нельзя"? К примеру — зачем вообще ввели какие-то шаблоны? Ведь можно очень удобно просто написать столько версий функции Max(), сколько типов аргументов нас интересует — не так ли?
Собственно, единственное, что делают шаблоны — сокращают объем записи для функций типа Max. Ничего другого они не делают.

А если копнуть чуть глубже, то окажется, что безо всех "дополнительных сущностей", внесённых в язык С++ со времён ассемблера, можно обойтись. Только строчек на нём потребуется немножко больше.

Да, на всякий случай поясню: я не предлагаю "тащить в язык всё, что в голову придёт". Любая фича должна начинать с "-500 баллов", чтобы этот барьер мешал попаданию мусора. Только если фича реально полезна, она наберёт баллов и попадёт в Долину Исполнения. Но твои критерии, имхо, чрезмерно строги.

На всякий случай напомню, что, как хорошо известно еще со времён тезиса Чёрча, обойтись можно вообще практически безо всего. Первый же тьюринг-полный язык программирования автоматически сделал все остальные языки ненужными. Поэтому мерятся они не тем, что "нового" можно сделать на этом языке, а тем, насколько мало усилий нужно для написания "старого". 100% фич реальных языков программирования сделаны только для сокращения количества строчек, и больше ни для чего.

PD>Я привел минимальный демонстрационный пример, который показывает тебе суть того, о чем я говорю, и я вовсе не собирался в нем писать больше, чем мне нужно для этого примера.

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

PD>Бесплатный сыр бывает только в мышеловках

Ну-ну. Предлагаю отказаться от бесплатного сыра типа компайл-тайм проверки типов или перегрузки операторов. Наверно, это мышеловка — надо вернуться в старый добрый K&R C без этих новомодных фишек вроде описания типов аргументов прямо в месте декларации функции.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Inline records
От: FR  
Дата: 13.10.09 15:55
Оценка:
Здравствуйте, netch80, Вы писали:


N>Полной эта схема будет тогда, когда можно будет где-то отдельно сформировать набор аргументов функции в виде кортежа и затем передать его в функцию. Аналоги — питоновый callable(*args) и эрланговый apply(Fun,Args) (в эрланге передаётся список, а не кортеж, но это тут менее важно). Не знаю, как оно сейчас в Nemerle, но описанного тут — явно недостаточно.


В D от digitalmars, в котором схема вполне аналогична немерлевской, можно без как проблем сформировать кортеж отдельно и передать в функцию, так и наоборот "разобрать" функцию на возвращаемое значение и кортеж параметров.
Re[5]: Inline records
От: Трурль  
Дата: 13.10.09 15:56
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А там есть definite assignment?

В стандарте не прописано, но обычно компиляторы проводят анализ.

S>Какие ограничения на использование b накладываются на тело GetSomeInt?

В в оригинальной аде в out-параметры можно только писать. В аде-95 разрешено и читать.
Re: Inline records
От: dneprq  
Дата: 13.10.09 16:02
Оценка:
Что такое — запись?
Re[2]: Inline records
От: FR  
Дата: 13.10.09 16:24
Оценка:
Здравствуйте, dneprq, Вы писали:

D>Что такое — запись?


http://msdn.microsoft.com/en-us/library/dd233184(VS.100).aspx
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 17:46
Оценка:
Здравствуйте, dneprq, Вы писали:

D>Что такое — запись?


Кортеж с именованными параметрами. От класса отличается главным образом тем, что как и кортеж поддерживает структурную идентичность. "структурная идентичность" означает, что два экземпляра двух типов имеющих одинаковую структуру (одинаковый список полей) идентичны и могут взаимозаменяемость. Кроме того у записей все поля публичны и записи могут описываться их конструктором (включающим инициализацию всех полей).

Более развернуто: Record.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 17:50
Оценка: +1
Здравствуйте, FR, Вы писали:

D>>Что такое — запись?


FR>http://msdn.microsoft.com/en-us/library/dd233184(VS.100).aspx


Эта ссылка может ввести в заблуждение. Тип записи может не иметь явного имени.

Я как раз, в своих рассуждениях, говорил именно о записях не имеющих явно выраженного типа.
Так что более верно было бы дать ссылку на описание записей в СУБД.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 18:01
Оценка:
Здравствуйте, netch80, Вы писали:

N>С какого-то количества элементов в ответе таки да (опыт того же Erlang). Просто примеры не совсем адекватны: пока этих элементов 2-3 — собеседники не видят смысла в подобной "хитрой штуке". На практике же дофига случаев, когда их и 10, и 30. Если их состав определён на этапе компиляции, то рисовать мап на них — нехорошо, лучше пользоваться именованными статическими позициями. По сказанному, я "издалека" поддерживаю эту идею.


На моей практике 10 и тем более 30 не встречалось ни разу. Но сути дела это, по-моему, не меняет.

N>Полной эта схема будет тогда, когда можно будет где-то отдельно сформировать набор аргументов функции в виде кортежа и затем передать его в функцию.


Это так и есть на практике. В немрле, ты можешь сделать так:
def x = ("aaa", 12);
f(x)



N>Аналоги — питоновый callable(*args) и эрланговый apply(Fun,Args) (в эрланге передаётся список, а не кортеж, но это тут менее важно). Не знаю, как оно сейчас в Nemerle, но описанного тут — явно недостаточно.


Это не аналогии. К функциям с переменным числом аргументов это отношения не имеет.
В статически типизированных языках список аргументов не может быть любым кортежем, так как иначе теряется та самая статическая типизация. Поэтому в них, для передачи динамического списка параметров, используется массив. Но массив не может иметь разные типы.

N>(К слову — тут явно не хватает ещё одного вида скобок для кортежей. Для сравнения, в Erlang — {...},


Наоборот. Это унификация. Тем самым показывается, что нет разницы между списком параметров и кортежем.

N>а в таком оригинальном языке как Рапира (на формирование которого явно повлияли какие-то ранние ФЯ) — <*...*>. В питоне обошлись круглыми скобками и поэтому для кортежа в один элемент используют синтаксический костыль)


Питен не типизированный язык, так что в нем массив трудно отличим от кортежа. Вобщем, со скриптами отдельный разговор. В них многое по-другому.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 18:23
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>В основном ты прав, но ИМХО все это эффекты второго порядка. Я согласен, что писать придется несколько иначе и эти эффекты надо учитывать. Но , на мой взгляд, эти отличия второго порядка недостаточны, чтобы вводить новое понятие в язык, поскольку пусть не на 100%, но на 99.9% мы уже имеем то же самое иным способом.


Это эффект первого порядка — immutability (неизменяемость). А все остальное — следствия.

Как не странно но неизменяемость дает не только большую безопасность, но и (зачастую) большую выразительность. Так что лично я предпочитаю использовать неизменяемость везде где это не вызывает каких-то проблем (в основном с производительностью).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 18:29
Оценка: -1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>И ИМХО не надо вводить в язык дополнительные сущности, пока не станет ясно, что без них обходиться нельзя. Именно нельзя. А не "мне хотелось бы это написать в одну строку, а не 3"


Тогда за фиг тебе С++? На С можно писать точно так же, только с большим количеством явно выраженных деталей. Ну, а раз можно обходиться, то нужно (по твоей логике)!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.10.09 19:52
Оценка:
Здравствуйте, VladD2, Вы писали:

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


N>>С какого-то количества элементов в ответе таки да (опыт того же Erlang). Просто примеры не совсем адекватны: пока этих элементов 2-3 — собеседники не видят смысла в подобной "хитрой штуке". На практике же дофига случаев, когда их и 10, и 30. Если их состав определён на этапе компиляции, то рисовать мап на них — нехорошо, лучше пользоваться именованными статическими позициями. По сказанному, я "издалека" поддерживаю эту идею.


VD>На моей практике 10 и тем более 30 не встречалось ни разу. Но сути дела это, по-моему, не меняет.


Суть — нет. Показательность примера для увода от посторонних обсуждений — да.

N>>Аналоги — питоновый callable(*args) и эрланговый apply(Fun,Args) (в эрланге передаётся список, а не кортеж, но это тут менее важно). Не знаю, как оно сейчас в Nemerle, но описанного тут — явно недостаточно.

VD>Это не аналогии. К функциям с переменным числом аргументов это отношения не имеет.

Описанное мной точно так же годится для функций с постоянным числом аргументов, и на практике так в основном и применяется. При чём тут переменное число аргументов?

N>>(К слову — тут явно не хватает ещё одного вида скобок для кортежей. Для сравнения, в Erlang — {...},

VD>Наоборот. Это унификация. Тем самым показывается, что нет разницы между списком параметров и кортежем.

Если нет разницы, то это уже не статическая типизация.

N>>а в таком оригинальном языке как Рапира (на формирование которого явно повлияли какие-то ранние ФЯ) — <*...*>. В питоне обошлись круглыми скобками и поэтому для кортежа в один элемент используют синтаксический костыль)


VD>Питен не типизированный язык,


Ну вот, кончил за упокой. С каких это пор он не типизированный?

VD> так что в нем массив трудно отличим от кортежа.


В нём вообще массива нет, а если ты про список — отличия очень заметные.

VD> Вобщем, со скриптами отдельный разговор. В них многое по-другому.


Как-то ты походя прокомментировал все "скрипты", несмотря на то, что между ними порой больше разницы, чем с тем же Nemerle...
The God is real, unless declared integer.
Re[6]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 20:32
Оценка:
Здравствуйте, netch80, Вы писали:

N>>>Аналоги — питоновый callable(*args) и эрланговый apply(Fun,Args) (в эрланге передаётся список, а не кортеж, но это тут менее важно). Не знаю, как оно сейчас в Nemerle, но описанного тут — явно недостаточно.

VD>>Это не аналогии. К функциям с переменным числом аргументов это отношения не имеет.

N>Описанное мной точно так же годится для функций с постоянным числом аргументов, и на практике так в основном и применяется. При чём тут переменное число аргументов?


А разве *args — это не оно?

N>>>(К слову — тут явно не хватает ещё одного вида скобок для кортежей. Для сравнения, в Erlang — {...},

VD>>Наоборот. Это унификация. Тем самым показывается, что нет разницы между списком параметров и кортежем.

N>Если нет разницы, то это уже не статическая типизация.


Ложное утверждение.

N>>>а в таком оригинальном языке как Рапира (на формирование которого явно повлияли какие-то ранние ФЯ) — <*...*>. В питоне обошлись круглыми скобками и поэтому для кортежа в один элемент используют синтаксический костыль)


VD>>Питен не типизированный язык,


N>Ну вот, кончил за упокой. С каких это пор он не типизированный?


С рождения.

Ладно. Такой уровень дисскуссии меня не устраиват. За сим откланиваюсь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.10.09 21:08
Оценка:
Здравствуйте, VladD2, Вы писали:

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


N>>>>Аналоги — питоновый callable(*args) и эрланговый apply(Fun,Args) (в эрланге передаётся список, а не кортеж, но это тут менее важно). Не знаю, как оно сейчас в Nemerle, но описанного тут — явно недостаточно.

VD>>>Это не аналогии. К функциям с переменным числом аргументов это отношения не имеет.

N>>Описанное мной точно так же годится для функций с постоянным числом аргументов, и на практике так в основном и применяется. При чём тут переменное число аргументов?


VD>А разве *args — это не оно?


Нет. То, что ты имеешь в виду — в синтаксисе определения функции. А я говорил про синтаксис вызова.

VD>>>Питен не типизированный язык,

N>>Ну вот, кончил за упокой. С каких это пор он не типизированный?
VD>С рождения.
VD>Ладно. Такой уровень дисскуссии меня не устраиват. За сим откланиваюсь.

Ну куда нам, убогим, до твоего божественного величества.
The God is real, unless declared integer.
Re[8]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 21:27
Оценка: +1
Здравствуйте, netch80, Вы писали:

VD>>>>Питен не типизированный язык,

N>>>Ну вот, кончил за упокой. С каких это пор он не типизированный?
VD>>С рождения.
VD>>Ладно. Такой уровень дисскуссии меня не устраиват. За сим откланиваюсь.

N>Ну куда нам, убогим, до твоего божественного величества.


Незнаю причемт тут величие, но когда начинают оспариваться базовые вещи или начинают докапываться до терминов, дисскссия для меня теряет всяческий интерес. В данном случае совершенно точно имеет факт одного из двух.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.10.09 21:39
Оценка:
Здравствуйте, VladD2, Вы писали:

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


VD>>>>>Питен не типизированный язык,

N>>>>Ну вот, кончил за упокой. С каких это пор он не типизированный?
VD>>>С рождения.
VD>>>Ладно. Такой уровень дисскуссии меня не устраиват. За сим откланиваюсь.

N>>Ну куда нам, убогим, до твоего божественного величества.


VD>Незнаю причемт тут величие, но когда начинают оспариваться базовые вещи или начинают докапываться до терминов, дисскссия для меня теряет всяческий интерес.


Это не "докапывание до терминов", это поиск согласования. Когда ты говоришь какие-то вещи безусловно не соответствующие общепринятому пониманию, "совершенно точно имеет факт одного из двух" — или непонимание, или своя терминология ради какой-то своей цели. Но причины такого определения ты раскрывать не хочешь — ну что ж, заставить нельзя:))

VD> В данном случае совершенно точно имеет факт одного из двух.


Мимо. Впрочем, возобновлять дискуссию мне облом.
The God is real, unless declared integer.
Re[11]: Inline records
От: Pavel Dvorkin Россия  
Дата: 14.10.09 01:49
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>Тогда за фиг тебе С++? На С можно писать точно так же, только с большим количеством явно выраженных деталей. Ну, а раз можно обходиться, то нужно (по твоей логике)!


Инкапсулирование. На С практически невозможно, разве что пародия.
Наследование. То же.
Полиморфизм. То же.
With best regards
Pavel Dvorkin
Re[11]: Inline records
От: Pavel Dvorkin Россия  
Дата: 14.10.09 02:15
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну, я так и подумал. Впрочем, мой аргумент ты очень хорошо подчеркнул — даже "минимальный демонстрационный" пример получился достаточно громоздким, чтобы объяснить сомневающимся все преимущества нормального решения.


Подумал еще раз. Сравним два решения (моих)


FPoint fp = GetPoint();

IPoint ip;
GetPointByRef(ip);
FPoint fp1 = ip;


Дьявол в деталях, ты прав. Вот и давай этого дьявола поищем тут.

Первый способ создания FPoint выглядит намного красивее ? Бесспорно. Но!

Тебе очевидно, глядя на эту единственную строчку, что здесь создается два экземпляра ? Один — промежуточный IPoint, возвращаемый GetPoint, другой — результат конструирования FPoint по этому промежуточному IPoint.
Мне — не очевидно. Глядя на этот код, я не знаю этого. И компилятор мне не поможет — конструктор такой есть, все честно.

А вот во втором варианте кода это ясно даже младенцу. И глядя на него, я задамся вопросом — а нужен ли здесь этот IPoint ip вообще ? Может быть, это совсем ненужная промежуточная сущность, от которой можно в данном случае отказаться.


FPoint fp1;
GetFPointByRef(fp1);


и теперь нет второго экземпляра. Только один остался.

А если я вот так попробую

GetPointByRef(fp1);

то компилятор меня поправит — нельзя так! И правильно, что нельзя.

А может, IPoint вообще лишняя сущность ? Может, его лучше ликвидировать как класс совсем ? Действительно я не могу обойтись вещественными точками, и мне нужны еще целые ? Почему нужны ? Ответ на это вопрос , возможно, заставит меня пересмотреть то, что я делаю и упростить код.

Ну а если я все же приду к выводу, что IPoint нужен, и здесь действительно нужно такое конструирование двух объектов, а не одного... ну тогда будет 3 строчки вместо одной. Зато я ясно понимаю, что я тут делаю, а не полагаюсь на неявные преобразования, которых я тут не вижу и которые, возможно, совсем не так эффективны, как мне бы этого хотелось. И, если уж быть честным, неужели написание этих трех строчек по сравнению с той одной потребует каких-то выдающихся мысленных усилий ? И то примитивно, и это. Мысленные усилия понадобятся, когда придется придумать некий нетривиальный алгоритм или его реализовать. И там мне понадобится написать десятки и сотни строк, и их количество уменьшить не удастся, потому что там будут не столько описания переменных и конструирования, сколько сложная логика, которую упростить нельзя , потому что она определяется алгоритмом. А описание переменных и конструирование одной из другой ... господи, какие пустяки... было бы о чем говорить!


PD>>Бесплатный сыр бывает только в мышеловках

S>Ну-ну. Предлагаю отказаться от бесплатного сыра типа компайл-тайм проверки типов или перегрузки операторов. Наверно, это мышеловка — надо вернуться в старый добрый K&R C без этих новомодных фишек вроде описания типов аргументов прямо в месте декларации функции.

Неубедительно. От проверки типов в компайл-тайм никому хуже не будет, и никаких затрат в рантайме не потребуется. И от описания типов в заголовке вместо того, чтобы их описывать в стиле K&R — тоже не будет. Последнее — просто элементарное улучшение синтаксиса.
With best regards
Pavel Dvorkin
Re[9]: Inline records
От: Юрий Жмеренецкий ICQ 380412032
Дата: 14.10.09 03:15
Оценка:
Здравствуйте, Sinclair, Вы писали:

[...]

S>И вся эта могучая магия, надёжно отлаженная в поколениях компиляторов, мгновенно испаряется в тот момент, когда я хочу получить два значения вместо одного.


S>А вот более реалистичный пример

S>
S>(int modulo, int reminder) = IntDivide(arg, divisor); 
S>// не работает, если arg и divisor - byte; 
S>// при очевидном определении IntDivide как 
S>template<classname T>
S>void IntDivide(T arg, T divisor, T &modulo, T &remainder)
S>// вместо этого мне нужно вручную выписывать нудные промежуточные присваивания
S>// хотя вот такой код будет работать безо всяких проблем:
S>int modulo = arg / divisor; // здесь сработает очевидное расширение байта до инта
S>int remainder = arg % divisor; // но делить придётся дважды, вместо быстрой операции, которая получает сразу оба числа.
S>

S>Вот для таких случаев люди и хотят иметь возможность описывать туплы как общий случай возвращаемых скаляров.

На том же С++ реализуется достаточно просто — для тупла нужны шаблонные копирующий конструктор и оператор присваивания, которые почленно конструируют связку хранимых объектов — соответствующий конструктор/assigment operator будет выбран автоматически. При это скалярами ограничиваться необязательно — если тип T может быть сконструирован из типа U, то такое преобразование для каждого члена тупла будт использоваться автоматически.

Так сделано, например, в boost::tuple:

//...

template<class T>
tuple<T, T> f(T a, T b) //...

void g()
{
  typedef char byte;

  byte a = 17, b = 7;
  int i1, i2;
  double d1;
    
  tie(i1, i2) = f(a, b); // <byte, byte> -> <int, int>
  tie(i1, d1) = f(a, b); 
    
  tuple<float, float> t1 = f(a, b);   // <byte, byte> -> <float, float>
  tuple<int, float>   t2 = f(i1, i2);
}
Re[12]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 03:24
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>А вот во втором варианте кода это ясно даже младенцу. И глядя на него, я задамся вопросом — а нужен ли здесь этот IPoint ip вообще ? Может быть, это совсем ненужная промежуточная сущность, от которой можно в данном случае отказаться.

Ну, то есть понятно, что ты начнёшь задаваться лишними вопросами.

PD>А может, IPoint вообще лишняя сущность ? Может, его лучше ликвидировать как класс совсем ? Действительно я не могу обойтись вещественными точками, и мне нужны еще целые ? Почему нужны ? Ответ на это вопрос , возможно, заставит меня пересмотреть то, что я делаю и упростить код.

Это вряд ли. Пока что ты занимаешься тем, что только усложняешь код. У тебя была библиотечная функция, которая работала с целыми точками. Теперь ты её интегрируешь в программу, которая должна работать с вещественными. Какое "упрощение" ты хочешь сделать? Переписать готовую функцию, которая работала на быстрой целой арифметике, чтобы она теперь пользовалась медленной плавающей?

PD>Ну а если я все же приду к выводу, что IPoint нужен, и здесь действительно нужно такое конструирование двух объектов, а не одного... ну тогда будет 3 строчки вместо одной. Зато я ясно понимаю, что я тут делаю, а не полагаюсь на неявные преобразования, которых я тут не вижу и которые, возможно, совсем не так эффективны, как мне бы этого хотелось.


PD>И, если уж быть честным, неужели написание этих трех строчек по сравнению с той одной потребует каких-то выдающихся мысленных усилий ? И то примитивно, и это. Мысленные усилия понадобятся, когда придется придумать некий нетривиальный алгоритм или его реализовать. И там мне понадобится написать десятки и сотни строк, и их количество уменьшить не удастся, потому что там будут не столько описания переменных и конструирования, сколько сложная логика, которую упростить нельзя , потому что она определяется алгоритмом. А описание переменных и конструирование одной из другой ... господи, какие пустяки... было бы о чем говорить!

Ну, тогда давайте просто запретим все неявные приведения. Пусть будет по столько строчек, сколько надо. Вдруг эти приведения совсем не так эффективны, как тебе бы этого хотелось. Пусть компилятор ругается, когда ты пишешь
double d = GetSomeFloat();

Ок? Это ж пустяки — писать всякий раз явно new double(GetSomeFloat()).


PD>Неубедительно. От проверки типов в компайл-тайм никому хуже не будет, и никаких затрат в рантайме не потребуется.

Ну, то есть таки бывает бесплатный сыр?
PD> И от описания типов в заголовке вместо того, чтобы их описывать в стиле K&R — тоже не будет. Последнее — просто элементарное улучшение синтаксиса.
Правда что ли? А я полагал, что это элементарное улучшение семантики. Которое позволяет избежать ошибок при вызове функции — в K&R компилятор вообще ничего не компилирует, и нет проблем передать double вместо int.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 03:41
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>На том же С++ реализуется достаточно просто — для тупла нужны шаблонные копирующий конструктор и оператор присваивания, которые почленно конструируют связку хранимых объектов — соответствующий конструктор/assigment operator будет выбран автоматически. При это скалярами ограничиваться необязательно — если тип T может быть сконструирован из типа U, то такое преобразование для каждого члена тупла будт использоваться автоматически.


Всё не совсем так. То есть, это тоже реализация, хотя и совсем другая, чем предложил Павел.
Он, напомню, предложил считать запись (string ret1, int ret2) FuncName(string param1, int param2) эквивалентной void FuncName(string param1, int param2, out int ret2, out string ret1).
Такой подход всё еще позволяет направить результат функции в нужные локальные переменные (или поля доступного локально объекта), хотя по-прежнему сильно ограничен по сравнению с возвратом одного аргумента.

В твоём подходе мы требуем создавать именованный тип для возвращаемого значения.
Ну, то есть буст предлагает анонимный тупл с анонимными мемберами, но это всё еще не то, чего хочется.

Хочется, чтобы всё было в полный рост:
1. Возможность описывать имена компонентов тупла, чтобы пользователь меньше встревал. В подходе Павла реализуется само, через имена out-параметров. В твоём — нужно приватно наследоваться от анонимного тупла и задавать маппинг компонентов вручную
2. Возможность структурной эквивалентности
3. Еще всякие занятные возможности, которые скорее всего расходятся с идеями Влада.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Inline records
От: Pavel Dvorkin Россия  
Дата: 14.10.09 03:52
Оценка:
Здравствуйте, Sinclair, Вы писали:


PD>>А вот во втором варианте кода это ясно даже младенцу. И глядя на него, я задамся вопросом — а нужен ли здесь этот IPoint ip вообще ? Может быть, это совсем ненужная промежуточная сущность, от которой можно в данном случае отказаться.

S>Ну, то есть понятно, что ты начнёшь задаваться лишними вопросами.

А почему они лишние ? Аргументов пока не вижу.

PD>>А может, IPoint вообще лишняя сущность ? Может, его лучше ликвидировать как класс совсем ? Действительно я не могу обойтись вещественными точками, и мне нужны еще целые ? Почему нужны ? Ответ на это вопрос , возможно, заставит меня пересмотреть то, что я делаю и упростить код.

S>Это вряд ли. Пока что ты занимаешься тем, что только усложняешь код. У тебя была библиотечная функция, которая работала с целыми точками. Теперь ты её интегрируешь в программу, которая должна работать с вещественными. Какое "упрощение" ты хочешь сделать? Переписать готовую функцию, которая работала на быстрой целой арифметике, чтобы она теперь пользовалась медленной плавающей?

При чем тут быстрые и медленные целые и плавающие точки ? Мы что обсуждаем, скорость целой арифметики по сравнению с вещественной ? Речь вовсе не об этом, а о том, что создается 2 экземпляра. И во втором варианте этот факт присутствует явно, а в первом это можно легко упустить из виду. Написать очень красивую конструкцию, в одну строчку, а в ней такой дьявол порылся, что в действительности там несколько промежуточных экземпляров создается, память тратится и время. Так я предпочитаю явно видеть, что они создаются, тогда я еще подумаю, надо ли. И если все же надо (применительно к моему примеру это значит, что выкинуть IPoint нельзя), то преобразования я напишу явно, так что буду уверен, что это именно то преобразование, которое здесь уместно, а не то, которое здесь получилось из-за того, что я (допустим) неправильно понимаю, какие там в этих классах преобразования есть.

S>Ну, тогда давайте просто запретим все неявные приведения. Пусть будет по столько строчек, сколько надо. Вдруг эти приведения совсем не так эффективны, как тебе бы этого хотелось. Пусть компилятор ругается, когда ты пишешь

S>
S>double d = GetSomeFloat();
S>

S>Ок? Это ж пустяки — писать всякий раз явно new double(GetSomeFloat()).

Господи, зачем же до абсурда все доводить ? Я разве предложил запретить ? Да бога ради, пусть будет. И с float в double, и c IPoint в FPoint. Я просто говорю, что ИМХО лучше в более или менее сложных случаях использовать явные преобразования, так как при этом ясно, что происходит в действительности. И тому, кто пишет, и тому, кто читает. А плата ничтожная — подумаешь, вместо одной строчки 3 написать! Тем более что пишется и то и другое почти на автомате.

А эффективность явного и неявного преобразования — одна и та же. По той простой причине, что код один и тот же фактически. Как ни крути, а без конструктора FPoint(IPoint&) никуда не денешься, и он в обоих случаях вызывается.


PD>>Неубедительно. От проверки типов в компайл-тайм никому хуже не будет, и никаких затрат в рантайме не потребуется.

S>Ну, то есть таки бывает бесплатный сыр?

Нет. Просто в С не продумали это как следует, а в С++ исправили. Или, если хочешь, в С руководствовались принципом "да позволим как можно больше", а в С++ — "не позволи то, что слишком рискованно".

PD>> И от описания типов в заголовке вместо того, чтобы их описывать в стиле K&R — тоже не будет. Последнее — просто элементарное улучшение синтаксиса.

S>Правда что ли? А я полагал, что это элементарное улучшение семантики. Которое позволяет избежать ошибок при вызове функции — в K&R компилятор вообще ничего не компилирует, и нет проблем передать double вместо int.

Или ты не в курсе про lint, или сознательно делаешь вид, что не в курсе. Там именно рекомендовалось обязательно использовать lint для такой проверки. Фактически то же самое, но проверку передали сторонней утилите. Не лучшее решение, вот и поправили.
With best regards
Pavel Dvorkin
Re[14]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 04:19
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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

Тогда напиши явно, что ты имел в виду под "Ответ на это вопрос , возможно, заставит меня пересмотреть то, что я делаю и упростить код."
Я так понял, что ты решишь, что целые точки тебе не нужны, и откажешься от их использования. Другого способа отказаться в данном случае я не вижу.
Наверное, я что-то додумал за тебя. Ок, давай — объясни мне, как простыни бессмысленного boilerplate кода помогут тебе найти узкие места в программе. Кстати, ты каким профайлером пользуешься при оптимизации?

PD>И во втором варианте этот факт присутствует явно, а в первом это можно легко упустить из виду. Написать очень красивую конструкцию, в одну строчку, а в ней такой дьявол порылся, что в действительности там несколько промежуточных экземпляров создается, память тратится и время. Так я предпочитаю явно видеть, что они создаются, тогда я еще подумаю, надо ли.

Еще раз спрашиваю: почему в случае
double x = GetSomeFloat()

тебя не беспокоит создание промежуточных экземпляров, а в случае
(double x, double y) = GetFloatPoint()

уже начинает беспокоить? Где граница?

PD>А эффективность явного и неявного преобразования — одна и та же. По той простой причине, что код один и тот же фактически. Как ни крути, а без конструктора FPoint(IPoint&) никуда не денешься, и он в обоих случаях вызывается.

Ну, в каком-то смысле никуда не денешься. Но, поскольку компилятор C++ достаточно умён, то "вызов конструктора" превратится просто в пару-тройку ассемблерных инструкций, которые выполняют необходимые преобразования. Всё это будет агрессивно инлайниться. То есть abstraction penalty там снижен насколько возможно.

PD>Нет. Просто в С не продумали это как следует, а в С++ исправили. Или, если хочешь, в С руководствовались принципом "да позволим как можно больше", а в С++ — "не позволи то, что слишком рискованно".

Нет, ты всё же определись, есть бесплатный сыр или его нету. Если нету — то все эти новомодные трюки С++ явная мышеловка.
А если есть, то надо понять, откуда вдруг такое предубеждение против всего, чего нету в сегодняшнем С++.


PD>Или ты не в курсе про lint, или сознательно делаешь вид, что не в курсе. Там именно рекомендовалось обязательно использовать lint для такой проверки. Фактически то же самое, но проверку передали сторонней утилите. Не лучшее решение, вот и поправили.

Не, не в курсе. А что именно линт может проверить про K&R? Откуда у него информация из разных compilation unit?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 04:19
Оценка:
На тебе совсем уж клинический пример

class IPoint{
//...
};
class FPoint
// есть конструктор FPoint(IPoint&)
};
class FPoint3D
{
// есть конструктор FPoint3D(FPoint&)
};

IPoint GetPoint()
{
//
}

Все классы нужны. Ничего удалить нельзя.

FPoint3D fp = GetPoint();

Как красиво! Но дьявол в деталях — здесь три экземпляра создается.

А можно два. Изменим class FPoint3D, добавим конструктор FPoint3D(IPoint&)



И бесплатно, заметь.

А вот если бы я написал

IPoint ip;
GetPointByRef(ip);
FPoint fp1 = ip;
FPoint3D fp3D = fp1;

то я бы сразу задал себе вопрос — зачем я тут ерундой занимаюсь!

IPoint ip;
GetPointByRef(ip);
FPoint3D fp3D = ip;
With best regards
Pavel Dvorkin
Re[15]: Inline records
От: Pavel Dvorkin Россия  
Дата: 14.10.09 04:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


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

S>Тогда напиши явно, что ты имел в виду под "Ответ на это вопрос , возможно, заставит меня пересмотреть то, что я делаю и упростить код."
S>Я так понял, что ты решишь, что целые точки тебе не нужны, и откажешься от их использования. Другого способа отказаться в данном случае я не вижу.

Совершенно верно. Уточню — либо вообще, либо в данном месте.

S>Наверное, я что-то додумал за тебя. Ок, давай — объясни мне, как простыни бессмысленного boilerplate кода помогут тебе найти узкие места в программе.


Я не знаю, что такое бессмысленный boilerplate код, я просто вижу здесь ненужные действия. Подчеркиваю — просто ненужные. Впрочем, лучше смотри мой новый пример, там еще резче. И делать ненужные действия — не нужно. В любом случае.

>Кстати, ты каким профайлером пользуешься при оптимизации?


1.Встроенным в VS2008, хоть и не в восторге от него.
2. QueryPerfomanceCounter + fprintf

PD>>И во втором варианте этот факт присутствует явно, а в первом это можно легко упустить из виду. Написать очень красивую конструкцию, в одну строчку, а в ней такой дьявол порылся, что в действительности там несколько промежуточных экземпляров создается, память тратится и время. Так я предпочитаю явно видеть, что они создаются, тогда я еще подумаю, надо ли.

S>Еще раз спрашиваю: почему в случае
S>
S>double x = GetSomeFloat()
S>

S>тебя не беспокоит создание промежуточных экземпляров, а в случае
S>
S>(double x, double y) = GetFloatPoint()
S>

S>уже начинает беспокоить? Где граница?

Почему ты решил, что в этом примере с double- float меня не беспокоит ? Вполне возможно, что и беспокоит. Вполне возможно, что я решу, что лучше все на double перевести. От задачи зависит. Есть лишь один маленький фактор — здесь крайне мало шансов сделать ошибку, так как преобразование от float к doublt не есть used-defined, и у меня практически нет шансов сделать неправильное преобразование. Для user-defined у меня шансы есть , а при множественных преобразованиях их много

PD>>А эффективность явного и неявного преобразования — одна и та же. По той простой причине, что код один и тот же фактически. Как ни крути, а без конструктора FPoint(IPoint&) никуда не денешься, и он в обоих случаях вызывается.

S>Ну, в каком-то смысле никуда не денешься. Но, поскольку компилятор C++ достаточно умён, то "вызов конструктора" превратится просто в пару-тройку ассемблерных инструкций, которые выполняют необходимые преобразования. Всё это будет агрессивно инлайниться. То есть abstraction penalty там снижен насколько возможно.

Я же и говорю — здесь нет разницы. А вот создать один или 2 экземпляра — есть.

PD>>Нет. Просто в С не продумали это как следует, а в С++ исправили. Или, если хочешь, в С руководствовались принципом "да позволим как можно больше", а в С++ — "не позволи то, что слишком рискованно".

S>Нет, ты всё же определись, есть бесплатный сыр или его нету. Если нету — то все эти новомодные трюки С++ явная мышеловка.

OK. Допустим, при разработке первой версии языка X было принято неудачное решение. В версии 2 его поправили. По моему определению это не есть бесплатный сыр, так как нового ничего не внесли.

S>А если есть, то надо понять, откуда вдруг такое предубеждение против всего, чего нету в сегодняшнем С++.


Очень серьезный вопрос. Да, есть такое предубеждение. Но отвечать сейчас не буду, так как за минуту не ответишь.


PD>>Или ты не в курсе про lint, или сознательно делаешь вид, что не в курсе. Там именно рекомендовалось обязательно использовать lint для такой проверки. Фактически то же самое, но проверку передали сторонней утилите. Не лучшее решение, вот и поправили.

S>Не, не в курсе. А что именно линт может проверить про K&R? Откуда у него информация из разных compilation unit?

Программа lint может обрабатывать несколько заданных входных файлов и библиотек и проверять их на совместимость.

http://lib.ru/MAN/DEMOS210/lint.txt
With best regards
Pavel Dvorkin
Re[15]: еще один пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 04:59
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Все классы нужны. Ничего удалить нельзя.
Ок, это я могу понять.

PD> FPoint3D fp = GetPoint();

PD>Как красиво! Но дьявол в деталях — здесь три экземпляра создается.
А с чего ты это взял? Посмотри дизассемблером на результат работы современного компилятора. Там может вообще не создаваться никаких экземпляров.

PD>А вот если бы я написал


PD> IPoint ip;

PD> GetPointByRef(ip);
PD> FPoint fp1 = ip;
PD> FPoint3D fp3D = fp1;

PD>то я бы сразу задал себе вопрос — зачем я тут ерундой занимаюсь!


PD> IPoint ip;

PD> GetPointByRef(ip);
PD> FPoint3D fp3D = ip;
Вот и я задаю тебе вопрос: зачем ты занимаешься ерундой в виде выписывания лишних строчек? Если их не удастся устранить — то об этом скажет профайлер.
А если удастся — то ты пишешь код, которого вообще "нет". Он не несёт никакой полезной информации — смысл действий от только затуманивает, на результат компиляции не влияет.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 05:16
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:

PD>>Как красиво! Но дьявол в деталях — здесь три экземпляра создается.
S>А с чего ты это взял? Посмотри дизассемблером на результат работы современного компилятора. Там может вообще не создаваться никаких экземпляров.

Может и не будет, а может и будет. Все зависит от того, что там в действительности за классы, и что с ними делается. Может быть, компилятор настолько умный, что в состоянии исправить мои неудачные действия. Но из этого не следует, что я должен их делать, а потом каждый раз смотреть по дизассемблеру, исправил он или нет. Тем более, что в дальнейшем небольшое изменение может привести к тому, что компилятор обработает это иначе. Лучше ненужных действий вообще не делать, тогда и смотреть не придется. .

Так что пока аргументов за то, что надо их по крайней мере в программе на ЯВУ создавать, я не вижу. Не нужны они — незачем и писать. Независимо от того, что сделает с этим или не сделает компилятор.

PD>>А вот если бы я написал


PD>> IPoint ip;

PD>> GetPointByRef(ip);
PD>> FPoint fp1 = ip;
PD>> FPoint3D fp3D = fp1;

PD>>то я бы сразу задал себе вопрос — зачем я тут ерундой занимаюсь!


PD>> IPoint ip;

PD>> GetPointByRef(ip);
PD>> FPoint3D fp3D = ip;
S>Вот и я задаю тебе вопрос: зачем ты занимаешься ерундой в виде выписывания лишних строчек? Если их не удастся устранить — то об этом скажет профайлер.

Можно поинтересоваться, как это профайлер скажет мне, удалось ли устранить строчки ? Дизассемблер — еще ладно, но профайлер ?
With best regards
Pavel Dvorkin
Re[17]: еще один пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 05:51
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Может и не будет, а может и будет. Все зависит от того, что там в действительности за классы, и что с ними делается. Может быть, компилятор настолько умный, что в состоянии исправить мои неудачные действия. Но из этого не следует, что я должен их делать, а потом каждый раз смотреть по дизассемблеру, исправил он или нет.

Но ты как раз предлагаешь именно их и делать!
Явное выписывание конструкторов временных объектов — это как раз неудачные действия.
PD>Тем более, что в дальнейшем небольшое изменение может привести к тому, что компилятор обработает это иначе. Лучше ненужных действий вообще не делать, тогда и смотреть не придется. .

PD>Так что пока аргументов за то, что надо их по крайней мере в программе на ЯВУ создавать, я не вижу. Не нужны они — незачем и писать. Независимо от того, что сделает с этим или не сделает компилятор.

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

PD>Можно поинтересоваться, как это профайлер скажет мне, удалось ли устранить строчки ? Дизассемблер — еще ладно, но профайлер ?

Профайлер явно покажет, сколько времени твоя программа проводит в каких фрагментах кода. Если какая-то строка была выброшена компилятором, то её профиль даст ноль затрат.

Сам по себе дизассемблер как инструмент понимания выбрасывания строчек не нужен — кошмарный на вид асм даёт часто лучшие результаты, чем "красивый" код, вылизанный руками.
Профайлер покажет ахтунг с промежуточными объектами независимо от того, сколько строчек исходного кода было посвящено этому ахтунгу — шесть или одна.
Нет ахтунга — нет повода задавать себе вопросы про временные объекты (именно поэтому я называю их лишними). Есть ахтунг — надо разбираться. Садиться и внимательно смотреть, куда девается время. Может, там надо вручную объяснить компилятору, что оба результата надо передавать через регистры, а не только первый. Может, там делаются глупости типа урезания float до int с последующим расширением обратно в double — всё это не нужно держать "в уме". Всё это станет ясным, как на ладони, после первого же запуска профайлера.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 06:22
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Может и не будет, а может и будет. Все зависит от того, что там в действительности за классы, и что с ними делается. Может быть, компилятор настолько умный, что в состоянии исправить мои неудачные действия. Но из этого не следует, что я должен их делать, а потом каждый раз смотреть по дизассемблеру, исправил он или нет.

S>Но ты как раз предлагаешь именно их и делать!
S>Явное выписывание конструкторов временных объектов — это как раз неудачные действия.

Что-то я тебя понимать перестал. Я именно о том и говрю, что эти временные объекты можно устранить!


PD>>Так что пока аргументов за то, что надо их по крайней мере в программе на ЯВУ создавать, я не вижу. Не нужны они — незачем и писать. Независимо от того, что сделает с этим или не сделает компилятор.

S>Совершенно верно. Но из корректной логики ты неожиданным образом делаешь некорректные выводы. Там, где я хочу не писать лишний код, ты настаиваешь на борьбе со средствами сокращения этого кода.

Опять-таки передержка. Я предлагаю не уделять основное влияние размеру кода на ЯВУ (==LOC), а подумать в основном, сколько там объектов создается и все ли они нужны. Да, я не за то, чтобы число LOC. Но уменьшая число LOC, мы не сокращаем код. Это вообще-то совсем разные вещи.


PD>>Можно поинтересоваться, как это профайлер скажет мне, удалось ли устранить строчки ? Дизассемблер — еще ладно, но профайлер ?

S>Профайлер явно покажет, сколько времени твоя программа проводит в каких фрагментах кода. Если какая-то строка была выброшена компилятором, то её профиль даст ноль затрат.

Эхе-хе. Ты хоть с оптимизирующим С++ знаком ? Он тебе запросто из 5 строк сделает кусок кода так, что определить в нем, какие команды относятся к первой строке, а какие — к последней, уже никто не сможет. Нет в релизе С++ построчного анализа, в принципе нет. И отладки нет, по большому счету, хотя ее можно вести, но Quick Watch показывает порой совершенную чушь, потому что и переменной такой как ячейки памяти-то нет.

S>Сам по себе дизассемблер как инструмент понимания выбрасывания строчек не нужен — кошмарный на вид асм даёт часто лучшие результаты, чем "красивый" код, вылизанный руками.


Бывает, не спорю.

S>Профайлер покажет ахтунг с промежуточными объектами независимо от того, сколько строчек исходного кода было посвящено этому ахтунгу — шесть или одна.

S>Нет ахтунга — нет повода задавать себе вопросы про временные объекты (именно поэтому я называю их лишними). Есть ахтунг — надо разбираться. Садиться и внимательно смотреть, куда девается время. Может, там надо вручную объяснить компилятору, что оба результата надо передавать через регистры, а не только первый. Может, там делаются глупости типа урезания float до int с последующим расширением обратно в double — всё это не нужно держать "в уме". Всё это станет ясным, как на ладони, после первого же запуска профайлера.

А можно про ахтунг поточнее ? Хотя бы определение дать ? Например, профайлер показал, что на эту функцию уходит 1% времени. Оптимизировать ее или нет ?
With best regards
Pavel Dvorkin
Re[19]: еще один пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 07:20
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Что-то я тебя понимать перестал. Я именно о том и говрю, что эти временные объекты можно устранить!
Нет. Ты говоришь, что вот такая запись:
IPoint ip;
GetPointByRef(ip);
FPoint fp1 = ip;

лучше, чем такая:
FPoint fp = GetPoint();

Потому, что в первом варианте ты явно расписываешь все промежуточные временные объекты. А я тебе объясняю, что бессмысленно говорить о промежуточных временных объектах — они являются артефактами реализации. Более того, вторая запись яснее говорит компилятору о твоих намерениях, увеличивая шансы на устранение бессмысленного кода по переливанию пустого в порожнее.

Или я тебя не так понял?

PD>Опять-таки передержка. Я предлагаю не уделять основное влияние размеру кода на ЯВУ (==LOC), а подумать в основном, сколько там объектов создается и все ли они нужны.

Не надо об этом думать. В этом коде может создаваться от нуля до двух лишних объектов. Важно об этом думать или нет — покажет профайлер.

PD>Да, я не за то, чтобы число LOC. Но уменьшая число LOC, мы не сокращаем код. Это вообще-то совсем разные вещи.

Мы сокращаем объём того, чему нужно уделять внимание.

PD>Эхе-хе. Ты хоть с оптимизирующим С++ знаком ?

Да. А вот в твоём знакомстве с ним я что-то сомневаюсь.
PD>А можно про ахтунг поточнее ? Хотя бы определение дать ? Например, профайлер показал, что на эту функцию уходит 1% времени. Оптимизировать ее или нет ?
Я тебе уже много раз всё объяснял. Если тебя устраивает, в общем, время выполнения задачи, то ничего и оптимизировать не надо. Если не устраивает, и надо отрабатывать в десять раз быстрее, то надо смотреть в профайл. Если там есть какое-то место, которое жрёт 80% времени, а эта строчка 1%, то конечно же её не надо оптимизировать. Устранение всех промежуточных объектов один хрен не поможет нам приблизиться к решению задачи. Надо оптимизировать то место, на которое уходит 80%.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[20]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 07:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:

PD>>Что-то я тебя понимать перестал. Я именно о том и говрю, что эти временные объекты можно устранить!
S>Нет. Ты говоришь, что вот такая запись:
S>
S>IPoint ip;
S>GetPointByRef(ip);
S>FPoint fp1 = ip;
S>

S>лучше, чем такая:
S>
S>FPoint fp = GetPoint();
S>

S>Потому, что в первом варианте ты явно расписываешь все промежуточные временные объекты. А я тебе объясняю, что бессмысленно говорить о промежуточных временных объектах — они являются артефактами реализации. Более того, вторая запись яснее говорит компилятору о твоих намерениях, увеличивая шансы на устранение бессмысленного кода по переливанию пустого в порожнее.

S>Или я тебя не так понял?


Понял ты правильно. Но я тебе еще один пример привел, где после написания кода

IPoint ip;
GetPointByRef(ip);
FPoint fp1 = ip;
FPoint3D fp3D = fp1;

становится ясно, что промежуточный класс вообще не нужен, а нужен иной конструктор. Его компилятор не сделает. И вообще говоря, это может быть совсем не простым действием, а именно, не исключено, что при конструировании промежуточного объекта не будут выполняться некие действия, которые совсем не нужны, если преоьразовывать прямо из IPoint в Point3D. Ты предлагаешь надеяться, что компилятор все суммеет оптимизировать. Я в этом в общем случае далеко не уверен.

PD>>Опять-таки передержка. Я предлагаю не уделять основное влияние размеру кода на ЯВУ (==LOC), а подумать в основном, сколько там объектов создается и все ли они нужны.

S>Не надо об этом думать. В этом коде может создаваться от нуля до двух лишних объектов. Важно об этом думать или нет — покажет профайлер.

Предлагаю это направление дискуссии закрыть. Я уже не раз говорил, что с такой постановкой я не согласен. Нет смысла продолжать.


PD>>Эхе-хе. Ты хоть с оптимизирующим С++ знаком ?

S>Да. А вот в твоём знакомстве с ним я что-то сомневаюсь.

Ну да, конечно, как я забыл о твоей методике ответов! Если ответить нечего по существу — надо сделать вид, что вопроса и не было, тихо убрать вопрос, а вместо этого бросить оппоненту обвинение в чем-нибудь, авось он будет опровергать. Не выйдет. Или ответь на то замечание, что я сделал, или я буду считать, что ты вынужден был признать свою неправоту.

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

PD>Эхе-хе. Ты хоть с оптимизирующим С++ знаком ? Он тебе запросто из 5 строк сделает кусок кода так, что определить в нем, какие команды относятся к первой строке, а какие — к последней, уже никто не сможет. Нет в релизе С++ построчного анализа, в принципе нет. И отладки нет, по большому счету, хотя ее можно вести, но Quick Watch показывает порой совершенную чушь, потому что и переменной такой как ячейки памяти-то нет.


Есть что по существу возразить ? А если нет — чего стоят твои заверения, что профайлер, дескать разберется на уровне строк ?

PD>>А можно про ахтунг поточнее ? Хотя бы определение дать ? Например, профайлер показал, что на эту функцию уходит 1% времени. Оптимизировать ее или нет ?

S>Я тебе уже много раз всё объяснял. Если тебя устраивает, в общем, время выполнения задачи, то ничего и оптимизировать не надо. Если не устраивает, и надо отрабатывать в десять раз быстрее, то надо смотреть в профайл. Если там есть какое-то место, которое жрёт 80% времени, а эта строчка 1%, то конечно же её не надо оптимизировать. Устранение всех промежуточных объектов один хрен не поможет нам приблизиться к решению задачи. Надо оптимизировать то место, на которое уходит 80%.

Да, с таким подходом только и делать серьезную работу. А мне , например, заказчик выразил благодарность за то, что я оптимизировал код на 1%. И оптимизировал я не то место, где 80% — там я ничего сделать не мог, а именно то, что 2% требовало (суммарно), а теперь 1%.

Не так уж это мало — 1%. Когда ПО работает не на одном компьютере, а на сотнях
With best regards
Pavel Dvorkin
Re[21]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 14.10.09 08:23
Оценка:
Вот тебе пример, доведенный до абсурда.

Дан линейный двунаправленный список. Надо его перевернуть.

Есть классы

Linked2List на базе ссылочной реализации
Array с возможностью роста
и все конструкторы

Linked2List reverseList = Linked2List(Array(sourceList));

то есть выкладываем список в массив, проходим его с конца, создаем новый список. Или не с конца, не важно. Словом, сначала в массив. а из него — новый список.

Сами по себе действия

выложить список в массив
создать список из массива

вполне осмысленны.

а их конкатенация применительно к данной задаче — абсурдна. Потому что задача решается намного проще. Без всякого выкладывания в массив.

И ты будешь утверждать, что некий компилятор будет настолько оптимизирующим, что разберется в том, что массив здесь не нужен, выкладывать не будет, а всесто этого сам построит код создания из списка реверсного списка ? Код, которого у меня в программе пока что и нет вообще ?
With best regards
Pavel Dvorkin
Re[3]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 14.10.09 08:27
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Кортеж с именованными параметрами. От класса отличается главным образом тем, что как и кортеж поддерживает структурную идентичность. "структурная идентичность" означает, что два экземпляра двух типов имеющих одинаковую структуру (одинаковый список полей) идентичны и могут взаимозаменяемость. Кроме того у записей все поля публичны и записи могут описываться их конструктором (включающим инициализацию всех полей).


Как это — кортеж поддерживает структурную идентичность? Разве кортеж — это не отдельный тип?

class Tuple2<A,B>
class Tuple3<A,B,C>
...


В Scala — так, в Nemerle по другому?
Re[11]: Inline records
От: Юрий Жмеренецкий ICQ 380412032
Дата: 14.10.09 08:29
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Всё не совсем так. То есть, это тоже реализация, хотя и совсем другая, чем предложил Павел.

S>Он, напомню, предложил считать запись (string ret1, int ret2) FuncName(string param1, int param2) эквивалентной void FuncName(string param1, int param2, out int ret2, out string ret1).

Ясно, не уловил акцента. Но out параметры — это в любом случае не выход.

S>В твоём подходе мы требуем создавать именованный тип для возвращаемого значения.

S>Ну, то есть буст предлагает анонимный тупл с анонимными мемберами, но это всё еще не то, чего хочется.

S>Хочется, чтобы всё было в полный рост:

S>1. Возможность описывать имена компонентов тупла, чтобы пользователь меньше встревал. В подходе Павла реализуется само, через имена out-параметров. В твоём — нужно приватно наследоваться от анонимного тупла и задавать маппинг компонентов вручную
S>2. Возможность структурной эквивалентности
S>3. Еще всякие занятные возможности, которые скорее всего расходятся с идеями Влада.

Какие? Поддержка IDE?
Мне, например, импонирует возможность вводить постусловия на результат, вроде таких:

(int x, int y : x < y) F();

Здесь и до зависимых типов недалеко... Можно разрешить использование 'this' и в том числе для использования при проверки предусловий для аргументов + состояние объекта, т.е.
void Container::Remove(Key k : !this.Empty && this.Exists(k))

Можно пойти еще дальше: операция Remove принимает (как бы) тупл из двух элементов — (Container, Key):
void Container::Remove(Container this_ : !this_.Empty, Key k : this_.Exists(k))

Компилятор, встречая выражение (без скобок) 'instance.Remove' может связать все вхождения this_ в списке аргументов и, по возможности, вычислить значения некоторых предикатов (здесь — '!this_.Empty'). Специфический такой карринг.

Одновременно с каждым объектом (instance) компилятору нужно носить (образно выражаясь), список предикатов (+значение), значения которых можно вычислить исходя из постусловий (на состояние). [Для Remove выше постусловие отсутствует для упрощения]. При несответствии значений сохраненного предиката и предиката, вычисленного при обнаружении попытки вызова, следует генерировать compile-time error.

Таким образом, имея предусловие для Remove и, например, такое объявление конструктора:

(this.Empty) Container::Container();

Такой код:
Container c;
c.Remove(key);

приведет к ошибке компиляции, т.к.

Container c; 
// в этой точке - c:{ this.Empty == true }

c.Remove // в этой произойдет обнаружение несоответствия ранее вычисленному значению


С 'Exists(k)' так просто не получится — но есть два варианта: самый простой — автоматически добавлять runtime проверку (Assert или что-то еще), второй — то же что и первый, + нужно исходить из того факта, что помимо совпадения произвольного ключа возможен случай с передачей в Remove значения, полученного от этого же экземпляра контейнера (или эквивалентного находящемуся в нем) — если между этими событиями модификация контейнера не производилась — есть 100% гарантия нахождения значения с указанным ключом в контейнере (можно анализировать и более сложные случаи). В этом случае проверка не нужна. Правда в этом случае компилятор должен поддерживать аннотирование любых объектов. Вобщем получаются predicate types.

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

Т.е. так нормально:
void f(int arg1, int arg2 = x, int arg3 = y);

// Отсутствующие значения принимают значения по умолчанию
f(arg1 = a, arg3 = b);

Но аналогия для возвращаемых значений тут неприменима. Да, можно игнорировать некоторые элементы, но активное использование такой 'фичи' больше похоже на симптомы проблем с дизайном.

Еще такой вариант использования из головы не выходит:
// Возвращает длину чего-то + что-то 
(int length, X x) f();

// В месте вызова эта длина используется как индекс 
(index, x) r = f();

Т.е. здесь фактически переименование.
Re[21]: еще один пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 09:04
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Понял ты правильно. Но я тебе еще один пример привел, где после написания кода


PD>IPoint ip;

PD>GetPointByRef(ip);
PD>FPoint fp1 = ip;
PD>FPoint3D fp3D = fp1;

PD>становится ясно, что промежуточный класс вообще не нужен, а нужен иной конструктор. Его компилятор не сделает.

OMG. Павел, ты становишься навязчивым. Ничего здесь не становится ясно! Зачем здесь "Иной Конструктор"?
Компилятор обычно достаточно умён для того, чтобы устранить все неиспользуемые промежуточные объекты. Он самостоятельно догадается заменить последовательность присваиваний на одно присваивание.
PD>И вообще говоря, это может быть совсем не простым действием, а именно, не исключено, что при конструировании промежуточного объекта не будут выполняться некие действия, которые совсем не нужны, если преоьразовывать прямо из IPoint в Point3D. Ты предлагаешь надеяться, что компилятор все суммеет оптимизировать. Я в этом в общем случае далеко не уверен.
Я уверен, что в общем случае надо полагаться на компилятор. А вот в частных случаях, когда компилятор не справился, есть два варианта:

1. Профайлер показал, что это имеет какое-то значение для результирующей производительности.
Вот тогда мы начинаем заморачиваться частичными специализациями и дополнительными конструкторами, чтобы помочь компилятору выполнить его работу.
Заметь, что новый конструктор FPoint3d(const IPoint& ip) автоматически будет выбран во всех местах, где написано однострочное присваивание. А твои "вручную выписанные три строки" придётся во всех местах править вручную, даже после введения такого конструктора. Epic Fail.

2. Профайлер показал, что это никакого значения для результирующей производительности не имеет.
Ну и хрен тогда с ними, с этими промежуточными объектами. Нет повода тратить своё и чужое время на ненужную работу.


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


PD>>Эхе-хе. Ты хоть с оптимизирующим С++ знаком ? Он тебе запросто из 5 строк сделает кусок кода так, что определить в нем, какие команды относятся к первой строке, а какие — к последней, уже никто не сможет. Нет в релизе С++ построчного анализа, в принципе нет. И отладки нет, по большому счету, хотя ее можно вести, но Quick Watch показывает порой совершенную чушь, потому что и переменной такой как ячейки памяти-то нет.


PD>Есть что по существу возразить ? А если нет — чего стоят твои заверения, что профайлер, дескать разберется на уровне строк ?

На что возразить? Вопроса никакого ты не задал. Точнее, на тот вопрос, который ты задал — я ответил. Ок, не для тебя, для других, разжую:
Нет, Павел, я ничуть не возражаю против этого потока сознания про поведение оптимизирующего компилятора.

Я возражаю против того, когда ты одной рукой пишешь "оптимизатор всё равно перепашет код так, что фактически выполняемые вычисления не удастся сопоставить с исходным кодом". А второй рукой пишешь "расписывая построчно детальный код присваиваний неявных промежуточных объектов друг другу я могу определить, где выполняются лишние вычисления".

Нужно выбрать какую-то одну из этих взаимоисключающих точек зрения. Это понятно?

PD>Не так уж это мало — 1%. Когда ПО работает не на одном компьютере, а на сотнях

Наверное, оптимизация получилась только благодаря тому, что ты не пользовался неявными преобразованиями. Ну и слава байту на этом.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: доведение ad absurdum
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 09:04
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Linked2List на базе ссылочной реализации

PD>Array с возможностью роста
PD>и все конструкторы
PD>Linked2List reverseList = Linked2List(Array(sourceList));
Не вижу здесь реверса. Вижу неэффективное копирование одного списка в другой через явно указанный промежуточный массив. Разработчика — уволить, код — заменить.

PD>И ты будешь утверждать, что некий компилятор будет настолько оптимизирующим, что разберется в том, что массив здесь не нужен, выкладывать не будет, а всесто этого сам построит код создания из списка реверсного списка ? Код, которого у меня в программе пока что и нет вообще ?

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

Если твой пример доработать до правдоподобия, то это будет выглядеть примерно так:
var reverseList = sourceList.Reverse();

Здесь, допустим, выбирается неэффективный дефолтный extension method который устроен примерно так:
public static L Reverse<L, T>(this L source)
  where L: IEnumerable<T>
(
    T[] temp = source.ToArray();
    Array.Reverse(temp); 
    if (typeof(L).IsAssignableFrom(temp.GetType)
        return (L) temp;
    else
        return (L)Activator.CreateInstance(typeof(L), temp); 
)

Великая сила этого метода — в том, что он прекрасно работает с любыми потомками IEnumerable<T>, а для массивов еще и достаточно эффективен.
Cлабость этого метода — понятно в чём. Естественно, никакой компилятор не поможет нам сгенерировать эффективный разворачивальщик двунаправленного списка, если его не было.
Зато как только мы заметили такую фигню, мы тут же бежим в реализацию класса Linked2List и реализуем эффективный экземплярный вариант метода Reverse.
Теперь магическим образом компилятор перестаёт выбирать неэффективную реализаци в исходной строчке и во всех местах, где вызывался Reverse.

А вот с тем корявым кодом, который написал ты, компилятор ничего сделать, увы, не в состоянии. Потому что там явно сказано "выкладывай в массив, не умничай".
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[12]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 09:38
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Какие? Поддержка IDE?

Нет. В императивном языке мне нравится идея не просто "возвращать кортеж", или "возвращать кортеж в анонимный кортеж, описанный по месту", а возможность "указывать, куда девать результаты".
Ну вот если в "обычном" коде мы пишем
var i = SomeFunc(a, b);
GlobalSettings.Instance.Property = SomeOtherFunc(a, b);

то как бы хочется аналогичных мощностей для
(var i, GlobalSettings.Instance.Property) = CombinedFunc(a, b);


ЮЖ>
(int x, int y : x < y) F();

Давайте отделять структурные результаты и постусловия.
В том смысле, что эта фича, имхо, полностью ортогональна кортежам. Сначала надо научиться возвращать
int x : x >= 10 F();

А уже потом подумать про кортежи.

ЮЖ>Здесь и до зависимых типов недалеко... Можно разрешить использование 'this' и в том числе для использования при проверки предусловий для аргументов + состояние объекта, т.е.

ЮЖ>
ЮЖ>void Container::Remove(Key k : !this.Empty && this.Exists(k))
ЮЖ>

Ну вот — видишь, здесь никаких кортежей нет, а предусловия какбы есть.

ЮЖ>Можно пойти еще дальше: операция Remove принимает (как бы) тупл из двух элементов — (Container, Key):

ЮЖ>
ЮЖ>void Container::Remove(Container this_ : !this_.Empty, Key k : this_.Exists(k))
ЮЖ>

Можно, а зачем?

ЮЖ>Одновременно с каждым объектом (instance) компилятору нужно носить (образно выражаясь), список предикатов (+значение), значения которых можно вычислить исходя из постусловий (на состояние). [Для Remove выше постусловие отсутствует для упрощения]. При несответствии значений сохраненного предиката и предиката, вычисленного при обнаружении попытки вызова, следует генерировать compile-time error.

Ну, вот это как бы уже совсем-совсем круто. Я про ЗТ еще ничего не читал, кроме местных дискуссий.
В принципе, понятно, что они позволяют (вроде бы) такие штуки. Но до конца неясны пределы, и то, где проводить границы.

ЮЖ>С 'Exists(k)' так просто не получится — но есть два варианта: самый простой — автоматически добавлять runtime проверку (Assert или что-то еще), второй — то же что и первый, + нужно исходить из того факта, что помимо совпадения произвольного ключа возможен случай с передачей в Remove значения, полученного от этого же экземпляра контейнера (или эквивалентного находящемуся в нем) — если между этими событиями модификация контейнера не производилась — есть 100% гарантия нахождения значения с указанным ключом в контейнере (можно анализировать и более сложные случаи). В этом случае проверка не нужна. Правда в этом случае компилятор должен поддерживать аннотирование любых объектов. Вобщем получаются predicate types.

Получится очень просто: либо компилятор статически может проверить гарантию наличия значения по ключу, либо пользователь обязан явно описать уточнение.
То есть вот такое примерно должно иметь место:
public void Test(Dictionary<string, int> data)
{
  data["test"] = GetExternalInt(); // тип data неявно сменился
  
  int a = data["test"]; // компилятор пропускает, потому что там точно лежит int
  
  int b = data["fail"]; // компилятор ругается, потому что нет гарантии наличия ключа
  
  int? c = data["fail"]; // компилятор спокоен, потому что мы явно предусмотрели отсутствие ключа

  int d = 0;  
  if (data.Exists("fail")) // тип data неявно сменился 
    int d = data["fail"]; // компилятор спокоен, потому что в этой ветке есть гарантия наличия ключа
}


ЮЖ>Если же не рассматривать вышесказанное, то имхо, идея именования элементов туплов вполне жизнеспособна, но озвученные аналогии с именами для аргументов начинают немного хромать в присутствии аргументов по умолчанию (для полноты картины):

ЮЖ>Но аналогия для возвращаемых значений тут неприменима. Да, можно игнорировать некоторые элементы, но активное использование такой 'фичи' больше похоже на симптомы проблем с дизайном.
Не вижу никакой хромоты. Не все языки позволяют неявно игнорировать возвращаемые значения. Поэтому здесь как-то ожидается примерно такое:
(_, index, _) = SomeFunc(); // явное игнорирование 1го и 3го результатов

ЮЖ>Т.е. здесь фактически переименование.

Правильно. Вполне себе нормальное переименование.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[23]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 14.10.09 10:02
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Linked2List на базе ссылочной реализации

PD>>Array с возможностью роста
PD>>и все конструкторы
PD>>Linked2List reverseList = Linked2List(Array(sourceList));
S>Не вижу здесь реверса. Вижу неэффективное копирование одного списка в другой через явно указанный промежуточный массив. Разработчика — уволить, код — заменить.

Ну не видишь, и не надо. Подумай сам, может ли тут быть реверс. Может быть, и догадаешься. Его можно даже двумя способами сделать

PD>>И ты будешь утверждать, что некий компилятор будет настолько оптимизирующим, что разберется в том, что массив здесь не нужен, выкладывать не будет, а всесто этого сам построит код создания из списка реверсного списка ? Код, которого у меня в программе пока что и нет вообще ?


Остальное skipped, ввиду того, что ты ушел в сторону. Вопрос не в том, как именно делать Reverse и какие тут extension (это пиши как хочешь), а в том. что не может компилятор за тебя этот код оптимизировать. Я же ясно сказал — ad absurdum. Пример не для того сделан, чтобы его улучшать (это я и сам бы мог), а просто для того, чтобы показать тебе еще раз твою ошибку в утверждении , что компилятор сможет сам все оптимизировать.
With best regards
Pavel Dvorkin
Re[22]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 10:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>OMG. Павел, ты становишься навязчивым.


Ну а ты делаешь вид, что являешься непонятливым.

> Ничего здесь не становится ясно! Зачем здесь "Иной Конструктор"?

S>Компилятор обычно достаточно умён для того, чтобы устранить все неиспользуемые промежуточные объекты.

Подменяешь понятия. Устранить неиспользуемые — сможет. Вообще не используемые. Но здесь не о том речь
Я тебе привел пример ad absurdum. Еще раз — не обсуждаем качество его реализации. Но в нем делается создание промежуточного массива и компилятор не сможет устранить его.

>Он самостоятельно догадается заменить последовательность присваиваний на одно присваивание.


Это да. Потому что примитивно. А вот заменить самостоятельно выкладывания списка в массив и обратно не сможет.

S>Я уверен, что в общем случае надо полагаться на компилятор. А вот в частных случаях, когда компилятор не справился, есть два варианта:


S>1. Профайлер показал, что это имеет какое-то значение для результирующей производительности.

S>Вот тогда мы начинаем заморачиваться частичными специализациями и дополнительными конструкторами, чтобы помочь компилятору выполнить его работу.
S>Заметь, что новый конструктор FPoint3d(const IPoint& ip) автоматически будет выбран во всех местах, где написано однострочное присваивание. А твои "вручную выписанные три строки" придётся во всех местах править вручную, даже после введения такого конструктора.

Нет. Они будут написаны один раз, после чего (цитирую себя)

PD>то я бы сразу задал себе вопрос — зачем я тут ерундой занимаюсь!


и заменил.


S>2. Профайлер показал, что это никакого значения для результирующей производительности не имеет.

S>Ну и хрен тогда с ними, с этими промежуточными объектами. Нет повода тратить своё и чужое время на ненужную работу.





PD>>>Эхе-хе. Ты хоть с оптимизирующим С++ знаком ? Он тебе запросто из 5 строк сделает кусок кода так, что определить в нем, какие команды относятся к первой строке, а какие — к последней, уже никто не сможет. Нет в релизе С++ построчного анализа, в принципе нет. И отладки нет, по большому счету, хотя ее можно вести, но Quick Watch показывает порой совершенную чушь, потому что и переменной такой как ячейки памяти-то нет.


PD>>Есть что по существу возразить ? А если нет — чего стоят твои заверения, что профайлер, дескать разберется на уровне строк ?

S>На что возразить? Вопроса никакого ты не задал. Точнее, на тот вопрос, который ты задал — я ответил. Ок, не для тебя, для других, разжую:
S>Нет, Павел, я ничуть не возражаю против этого потока сознания про поведение оптимизирующего компилятора.

S>Я возражаю против того, когда ты одной рукой пишешь "оптимизатор всё равно перепашет код так, что фактически выполняемые вычисления не удастся сопоставить с исходным кодом". А второй рукой пишешь "расписывая построчно детальный код присваиваний неявных промежуточных объектов друг другу я могу определить, где выполняются лишние вычисления".


Так,ясно. Что я пишу и что не пишу — это один вопрос. А вот насчет того, что профайлер, по твоему мнению, может потом сопоставить профилируемый код со строчками текста, мы кажется, разобрались. Не может все-таки. Возразить тебе не удалось, решил поэтому на меня перевести, этот твой прием я хорошо знаю. Не получится. Констатирую : рассуждения твои о возможности профайлера определить, какие строки исполнялись, а какие нет, надо признать неудовлетворительными. В результате падают и все твои прочие выводы, на этой основе сделанные.
PD>>Не так уж это мало — 1%. Когда ПО работает не на одном компьютере, а на сотнях
S>Наверное, оптимизация получилась только благодаря тому, что ты не пользовался неявными преобразованиями.

Оптимизировал я не свой код, а он был на С, так что не могло там быть неявных преобразований. Да и не все ли равно тебе, как я оптимизировал ? Ты лучше на вопрос ответь — нужно ли оптимизировать, если удастся выиграть 1% ? Опять пытаешься уйти от вопроса.
With best regards
Pavel Dvorkin
Re[22]: доведение ad absurdum
От: palm mute  
Дата: 14.10.09 10:54
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>И ты будешь утверждать, что некий компилятор будет настолько оптимизирующим, что разберется в том, что массив здесь не нужен, выкладывать не будет, а всесто этого сам построит код создания из списка реверсного списка ? Код, которого у меня в программе пока что и нет вообще ?


Есть такие компиляторы, в которых оптимизатор — программируемый. Разработчики библиотек могут посказывать компилятору, какие выражения и как можно упрощать. Например, можно создать правило, что list.reverse().reverse() == list — вуаля, избавились от двух ненужных вызовов и двух лишних копий списка. Glasgow Haskell Compiler — как раз такой компилятор, и в важных библиотеках этим пользуются.
Re[24]: доведение ad absurdum
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 11:00
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Ну не видишь, и не надо. Подумай сам, может ли тут быть реверс.
Я подумал.
PD>Может быть, и догадаешься. Его можно даже двумя способами сделать
За оба — разработчика уволить. Здесь мы видим нарушение транзитивности конструкторов.

PD>Остальное skipped, ввиду того, что ты ушел в сторону. Вопрос не в том, как именно делать Reverse и какие тут extension (это пиши как хочешь), а в том. что не может компилятор за тебя этот код оптимизировать. Я же ясно сказал — ad absurdum. Пример не для того сделан, чтобы его улучшать (это я и сам бы мог), а просто для того, чтобы показать тебе еще раз твою ошибку в утверждении , что компилятор сможет сам все оптимизировать.

Я не делал такого утверждения. Я делал совсем другое утверждение. Что характерно, все твои примеры подтверждают мои тезисы и опровергают твои. За что большое спасибо.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[23]: еще один пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 11:00
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Подменяешь понятия. Устранить неиспользуемые — сможет. Вообще не используемые. Но здесь не о том речь

PD>Я тебе привел пример ad absurdum. Еще раз — не обсуждаем качество его реализации. Но в нем делается создание промежуточного массива и компилятор не сможет устранить его.
Здесь речь именно о том. Ты вспомни контекст разговора — речь шла про туплы и про способы возврата нескольких аргументов.

Я тебе продемонстрировал причины, по которым предлагаемые тобой варианты "записи на гипотетическом языке" не стоит делать эквивалетными.

Тогда ты сел на своего любимого конька и начал рассуждать про эффективность программ на несуществующем языке.

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

Я, однако, честно пытался объяснять тебе очевидные последствия тех или иных решений, которые возникают при дизайне языка. Видимо — зря, потому что ты только пытаешься увести разговор в сторону. Приводить фрагменты безумного кода, чтобы доказать мне неспособность компилятора справляться с простейшими случаями.
Зачем это всё? Чтобы только уйти от обсуждения основной темы?

PD>Это да. Потому что примитивно. А вот заменить самостоятельно выкладывания списка в массив и обратно не сможет.

1. Теоретически, в частных случаях — может.
2. Нас не интересует в данном контексте общий случай алгоритмической оптимизации. Всё, что мы обсуждаем — способность компилятора устранить конструирование временных объектах при возврате нескольких значений.


PD>Нет. Они будут написаны один раз, после чего (цитирую себя)

PD>и заменил.
По прежнему не понимаю, почему нельзя написать их 0 раз, сэкономив время себе, компилятору, и процессору.

PD>Так,ясно. Что я пишу и что не пишу — это один вопрос. А вот насчет того, что профайлер, по твоему мнению, может потом сопоставить профилируемый код со строчками текста, мы кажется, разобрались.

Ок, хорошо. Пусть не может потом профайлер сопоставить строчки кода. Профайлер зато единственный, кто может сопоставить эффективность двух решений.

PD>Не может все-таки. Возразить тебе не удалось, решил поэтому на меня перевести, этот твой прием я хорошо знаю. Не получится. Констатирую : рассуждения твои о возможности профайлера определить, какие строки исполнялись, а какие нет, надо признать неудовлетворительными. В результате падают и все твои прочие выводы, на этой основе сделанные.

Отлично. Увёл, значит, разговор в сторону. Теперь то, повлияют ли лишние строчки boilerplate-кода, написанные вручную, на скорость работы программы, зависит только от того, сможет ли профайлер сопоставить им какие-либо данные. Ну-ну. Удачи.

PD>Оптимизировал я не свой код, а он был на С, так что не могло там быть неявных преобразований. Да и не все ли равно тебе, как я оптимизировал ? Ты лучше на вопрос ответь — нужно ли оптимизировать, если удастся выиграть 1% ? Опять пытаешься уйти от вопроса.

В большинстве случаев — не нужно. 1% — это типичная погрешность измерений. Допускаю, что где-то, в отдельных случаях, это может играть какую-то роль.
Но опять же, получается, что полезность возврата одним махом множества значений теперь зависит только от того, надо ли оптимизировать 1%. Ну не бред ли?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[25]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 14.10.09 11:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>За оба — разработчика уволить. Здесь мы видим нарушение транзитивности конструкторов.


Слушай, ты наконец усвоишь одну простую вещь ? Я привожу демонстрационный пример. Он только для демонстрации предназначен, и только того, о чем речь идет. Специально для этой цели сделан. Ни на что большее он не претендует и никакие иные требования к нему не предъявляются. Неужели это не понятно ?

PD>>Остальное skipped, ввиду того, что ты ушел в сторону. Вопрос не в том, как именно делать Reverse и какие тут extension (это пиши как хочешь), а в том. что не может компилятор за тебя этот код оптимизировать. Я же ясно сказал — ad absurdum. Пример не для того сделан, чтобы его улучшать (это я и сам бы мог), а просто для того, чтобы показать тебе еще раз твою ошибку в утверждении , что компилятор сможет сам все оптимизировать.

S>Я не делал такого утверждения.

Ну-ну. Ладно, пусть желающие сами читают твои постинги, там ясно сказано, что ты утверждаешь. Мне лень искать и цитировать.

>Я делал совсем другое утверждение. Что характерно, все твои примеры подтверждают мои тезисы и опровергают твои. За что большое спасибо.


Ну да, проще всего заявить голословно что-нибудь. Если аргументов нет — это тоже решение.
With best regards
Pavel Dvorkin
Re[23]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 14.10.09 11:11
Оценка:
Здравствуйте, palm mute, Вы писали:

PM>Есть такие компиляторы, в которых оптимизатор — программируемый. Разработчики библиотек могут посказывать компилятору, какие выражения и как можно упрощать. Например, можно создать правило, что list.reverse().reverse() == list — вуаля, избавились от двух ненужных вызовов и двух лишних копий списка.


Хм. Из того, что list.reverse().reverse() == list не ясно все же, как быть, если мне нужен просто list.reverse(). Тут подсказать не удастся вроде бы, так ?


Glasgow Haskell Compiler — как раз такой компилятор, и в важных библиотеках этим пользуются.

Вполне допускаю, что это может в определенных случаях помочь. Хотя все же для традиционных компиляторов это не делается. Но дело даже не в этом. Если ты начнешь подсказки компилятору делать, то ты тем самым переложишь свою работу в другое место. Иными словами, кое-что ты не запрограммируешь в коде, а запрограммируешь как правило для компилятора. Может, это и упрощает работу иногда.
With best regards
Pavel Dvorkin
Re[24]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 11:32
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здесь речь именно о том. Ты вспомни контекст разговора — речь шла про туплы и про способы возврата нескольких аргументов.


Речь в нашем треде давно уже про это не идет. Речь идет о промежуточных переменных и вызове конструкторов для этого. И ты это прекрасно знаешь.

S>Я тебе продемонстрировал причины, по которым предлагаемые тобой варианты "записи на гипотетическом языке" не стоит делать эквивалетными.


S>Тогда ты сел на своего любимого конька и начал рассуждать про эффективность программ на несуществующем языке.


S>В принципе, абсурд начался уже тогда — потому как на несуществующем языке никакой эффективности у программ, понятное дело, быть не может.


Демагогия. Мы перешли давно на С++. И до поры до времени ты обсуждал именно мои С++ реализации всех трех точек. Тоггда это тебя почему-то не волновало, и про туплы ты уже не вспоминал. А сейчас решил вспомнить, поскольку зашел в тупик.

S>Зачем это всё? Чтобы только уйти от обсуждения основной темы?


Опять демагогия. Если уж ты хотел не уходить от основной темы, надо было предложить сразу либо не обсуждать эффективность в вопросе вложенных преобразований. Сразу. Я бы тогда и не стал продолжать. А заявлять это сейчас поздно — как говорят дети, играющие в футбол — "заиграно". Иными словами, после того, как мяч уже ввели от ворот, не обсуждают, нужно ли назначать угловой.

S>2. Нас не интересует в данном контексте общий случай алгоритмической оптимизации. Всё, что мы обсуждаем — способность компилятора устранить конструирование временных объектах при возврате нескольких значений.


А как же туплы ? Ты же именно их и хочешь только обсуждать ? А продолжаешь тему оптимизации.


S>По прежнему не понимаю, почему нельзя написать их 0 раз, сэкономив время себе, компилятору, и процессору.


А, прекрасно. Значит, N раз все же писать не буду. Один раз напишу, тут же пойму, исправлюсь, и больше писать не буду.
Ответ — если соображу вовремя — напишу 0 раз. Сразу без промежуточной переменной. Если не соображу — написав, нажму несколько раз Del, BackSpace, Shift-Ctrl-L и поправлю. Тебе надо подробно описать, как именно я код редактирую ?

PD>>Так,ясно. Что я пишу и что не пишу — это один вопрос. А вот насчет того, что профайлер, по твоему мнению, может потом сопоставить профилируемый код со строчками текста, мы кажется, разобрались.

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

Ну слава богу! Наконец-то ты сумел признать свою неправоту. Прогресс явный!

>Профайлер зато единственный, кто может сопоставить эффективность двух решений.


Хм... Да, если написать вариант с моими точками, то сможет. А вот в задаче с массивом-списком — я и сам могу сопоставить. Без профайлера и даже без запуска. Потому как нечего из Новосибирска в Омск через Владивосток, Нью-Йорк и Москву лететь. Тут и так все ясно. И даже если этот полет ты совершишь один раз в жизни, и даже если тебе на деньги и время наплевать, то все равно кроме как глупостью этот полет никак назвать нельзя.

PD>>Не может все-таки. Возразить тебе не удалось, решил поэтому на меня перевести, этот твой прием я хорошо знаю. Не получится. Констатирую : рассуждения твои о возможности профайлера определить, какие строки исполнялись, а какие нет, надо признать неудовлетворительными. В результате падают и все твои прочие выводы, на этой основе сделанные.

S>Отлично. Увёл, значит, разговор в сторону. Теперь то, повлияют ли лишние строчки boilerplate-кода, написанные вручную, на скорость работы программы, зависит только от того, сможет ли профайлер сопоставить им какие-либо данные. Ну-ну. Удачи.

Разговор именно ты и пытался увести. А теперь начинаешь опять свою любимую игру — приписывать мне то, чего я не говорил, и на этом основании заниматься демагогией.

PD>>Оптимизировал я не свой код, а он был на С, так что не могло там быть неявных преобразований. Да и не все ли равно тебе, как я оптимизировал ? Ты лучше на вопрос ответь — нужно ли оптимизировать, если удастся выиграть 1% ? Опять пытаешься уйти от вопроса.

S>В большинстве случаев — не нужно. 1% — это типичная погрешность измерений. Допускаю, что где-то, в отдельных случаях, это может играть какую-то роль.
S>Но опять же, получается, что полезность возврата одним махом множества значений теперь зависит только от того, надо ли оптимизировать 1%. Ну не бред ли?

Не бред, а фальшь. Твоя. Вопрос об том, надо ли оптимизировать 1%, я поставил безотносительно от чего бы то ни было. Просто — надо или нет. И множества значений (туплы) здесь абсолютно ни при чем.

Вот мои слова

PD>Да, с таким подходом только и делать серьезную работу. А мне , например, заказчик выразил благодарность за то, что я оптимизировал код на 1%. И оптимизировал я не то место, где 80% — там я ничего сделать не мог, а именно то, что 2% требовало (суммарно), а теперь 1%.

PD>Не так уж это мало — 1%. Когда ПО работает не на одном компьютере, а на сотнях

И как с этим соотносится "полезность возврата одним махом множества значений теперь зависит только от того, надо ли оптимизировать 1%" ? Где бузина и где дядька ?
With best regards
Pavel Dvorkin
Re[26]: доведение ad absurdum
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 11:36
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Слушай, ты наконец усвоишь одну простую вещь ? Я привожу демонстрационный пример. Он только для демонстрации предназначен, и только того, о чем речь идет. Специально для этой цели сделан. Ни на что большее он не претендует и никакие иные требования к нему не предъявляются. Неужели это не понятно ?
Я поясню: пока что компьютеры неспособны "перехитрить" человека. Поэтому всегда можно придумать "демонстрационный" контрпример.
Нас не интересует способность компилятора оптимизировать любые зловредно устроенные программы.
Нас интересует способность компилятора оптимизировать типичные решения, которые удобно писать людям.
Ты можешь писать сколько угодно примеров идиотского кода, который занимается нагреванием воздуха. Они ровным счётом ничего не доказывают и не опровергают.

Я тебе пишу реалистичные примеры того, какой код я хочу получать. Ты мне пытаешься доказать, что такая запись вредна из-за потенциальной неоптимальности. При этом маешься совершеннейшей дурью.
PD>Ну да, проще всего заявить голословно что-нибудь. Если аргументов нет — это тоже решение.
Я уже привёл массу аргументов, по буквам разжёвывая возможное поведение компилятора в различных сценариях.
Ты мне всё время приводишь примеры того, как можно
а) написать много лишнего кода, который всё равно выкинет компилятор
б) написать много лишнего кода, который бы всё равно был выкинут компилятором, и выкинуть его вручную
в) написать много лишнего кода, который компилятор не сможет выкинуть
г) написать много лишнего кода, который компилятор не сможет выкинуть, и выкинуть его вручную.

Ну так эти все примеры только подтверждают мою точку зрения: лучше всего — не писать лишнего кода. И только если лишний целевой будет сгенерирован компилятором, озаботиться исправлением исходника так, чтобы этого не происходило.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[25]: еще один пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 12:14
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Речь в нашем треде давно уже про это не идет. Речь идет о промежуточных переменных и вызове конструкторов для этого. И ты это прекрасно знаешь.

Нет, Павел. Не надо тут пытаться поймать меня на знании деталей С++. Я понимаю, что это твоя любимая тема, но обсуждаем мы не её.

PD>Демагогия. Мы перешли давно на С++. И до поры до времени ты обсуждал именно мои С++ реализации всех трех точек.

Поясняю: я пытался объяснить суть происходящего на понятном тебе языке. Я не виноват, что ты стал приводить примеры именно на С++.

И не я виноват, что именно ты привёл примеры, которые даже при компиляции на С++ подтверждают именно мою точку зрения.

Тебе пришлось сильно постараться, чтобы придумать пример, который (как ты думаешь) заведомо не получится оптимизировать компилятором.

PD>Опять демагогия. Если уж ты хотел не уходить от основной темы, надо было предложить сразу либо не обсуждать эффективность в вопросе вложенных преобразований. Сразу. Я бы тогда и не стал продолжать. А заявлять это сейчас поздно — как говорят дети, играющие в футбол — "заиграно". Иными словами, после того, как мяч уже ввели от ворот, не обсуждают, нужно ли назначать угловой.

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

PD>А как же туплы ? Ты же именно их и хочешь только обсуждать ? А продолжаешь тему оптимизации.

Это и есть про туплы. Сначала ты предложил вместо туплов сделать возврат кортежа синтаксическим сахаром для &-параметров. Я тебе подробно объяснил, в чём тут грабли.
Тогда ты взялся за возврат туплов со стороны "оптимизации", причём как обычно — приводя примеры, которые опровергают твою же точку зрения.

PD>А, прекрасно. Значит, N раз все же писать не буду. Один раз напишу, тут же пойму, исправлюсь, и больше писать не буду.

PD>Ответ — если соображу вовремя — напишу 0 раз. Сразу без промежуточной переменной. Если не соображу — написав, нажму несколько раз Del, BackSpace, Shift-Ctrl-L и поправлю. Тебе надо подробно описать, как именно я код редактирую ?
Нет, не надо. Всё понятно и так.

PD>Хм... Да, если написать вариант с моими точками, то сможет. А вот в задаче с массивом-списком — я и сам могу сопоставить.

Напомни, пожалуйста, какое отношение к туплам имеет задача с массивом-списком? Ну или, хотя бы, какое отношение она имеет к неявным преобразованиям?

PD>Без профайлера и даже без запуска. Потому как нечего из Новосибирска в Омск через Владивосток, Нью-Йорк и Москву лететь. Тут и так все ясно. И даже если этот полет ты совершишь один раз в жизни, и даже если тебе на деньги и время наплевать, то все равно кроме как глупостью этот полет никак назвать нельзя.

Но именно ты почему-то начал писать именно такой полёт. Посмотри в свой код — у тебя там явно сказано "лететь через Array". Подумай — зачем ты это написал? Как на самом деле выглядит код обращения такого списка — пусть такой, где никто не задумался об оптимизации заранее.
А то ведешь себя тут, как суровые сибирские мужики с японской бензопилой.

PD>Не бред, а фальшь. Твоя. Вопрос об том, надо ли оптимизировать 1%, я поставил безотносительно от чего бы то ни было. Просто — надо или нет. И множества значений (туплы) здесь абсолютно ни при чем.

Ок, давай тогда я спрошу безотносительно всего: нужно ли снимать кожуру с яблока? А если не ответишь — будем считать, что все твои выводы в этой ветке неверны. Раз уж пошла такая пьянка, задавать не относящиеся к делу вопросы.

PD>И как с этим соотносится "полезность возврата одним махом множества значений теперь зависит только от того, надо ли оптимизировать 1%" ? Где бузина и где дядька ?

Вот и я удивляюсь — начали с туплов, а закончили, как всегда, твоим 1%.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[27]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 14.10.09 12:29
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:

PD>>Слушай, ты наконец усвоишь одну простую вещь ? Я привожу демонстрационный пример. Он только для демонстрации предназначен, и только того, о чем речь идет. Специально для этой цели сделан. Ни на что большее он не претендует и никакие иные требования к нему не предъявляются. Неужели это не понятно ?
S>Я поясню: пока что компьютеры неспособны "перехитрить" человека. Поэтому всегда можно придумать "демонстрационный" контрпример.

И то хорошо, что ты это признал. Таким образом, по крайней мере для этого случая ты мою правоту признал. Прогресс.

S>Нас не интересует способность компилятора оптимизировать любые зловредно устроенные программы.


Демагогия. Никто ничего зловредного не обсуждал.

S>Нас интересует способность компилятора оптимизировать типичные решения, которые удобно писать людям.

S>Ты можешь писать сколько угодно примеров идиотского кода, который занимается нагреванием воздуха.

Демагогия.


S>Ты мне всё время приводишь примеры того, как можно

S>а) написать много лишнего кода, который всё равно выкинет компилятор

Не было.

S>б) написать много лишнего кода, который бы всё равно был выкинут компилятором, и выкинуть его вручную


Не было. Я как раз предлагал его не писать.

S>в) написать много лишнего кода, который компилятор не сможет выкинуть


Тоже ложь. Я как раз и предлагал его не писать.

S>г) написать много лишнего кода, который компилятор не сможет выкинуть, и выкинуть его вручную.


и опять ложь.

А правда лишь одна — я говорю, что при анализе кода в применении к вопросу о явных и неявных преобразованиях может выясниться, что кое-что делать совсем не надо, а поэтому не надо и писать. И это легче будет обнаружить при явных, нежели при неявных преобразованиях.
А мне в ответ — все, что угодно. Ладно. Оставайся при своем мнении.
With best regards
Pavel Dvorkin
Re[13]: Inline records
От: Юрий Жмеренецкий ICQ 380412032
Дата: 14.10.09 12:40
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Юрий Жмеренецкий, Вы писали:


ЮЖ>>Какие? Поддержка IDE?

S>Нет. В императивном языке мне нравится идея не просто "возвращать кортеж", или "возвращать кортеж в анонимный кортеж, описанный по месту", а возможность "указывать, куда девать результаты".
S>Ну вот если в "обычном" коде мы пишем
S>
S>var i = SomeFunc(a, b);
S>GlobalSettings.Instance.Property = SomeOtherFunc(a, b);
S>

S>то как бы хочется аналогичных мощностей для
S>
S>(var i, GlobalSettings.Instance.Property) = CombinedFunc(a, b);
S>


Хм... Но это как раз ортогонально именам членов тупла при декларации функции.

ЮЖ>>
(int x, int y : x < y) F();

S>Давайте отделять структурные результаты и постусловия.

У меня присутствует ощущение того, что если постусловие в отношении функции для такого кортежа (особенно если его члены именованы) равное 'true' (т.е. вообще любой набор значений является допустимым), то это следствие нарушения SRP. Это далеко не стопроцентный критерий, разумеется, но даже упомянутая выше функция IntDivide имеет постусловия в отношении результата 'в целом', т.е. фактически имеется связь между компонентами (в смысле cohesion).

С другой стороны я вижу сценарии, когда инварианты на кортеж не являются значимыми в точке использования, но это будут скорее утилитарные вещи, для которых достаточно индекснего доступа для отдельных элементов. Но в этом случае такой синтаксис:

(double x, double y) = GetXY()


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

S>В том смысле, что эта фича, имхо, полностью ортогональна кортежам.

S>Сначала надо научиться возвращать
S>
S>int x : x >= 10 F();
S>

S>А уже потом подумать про кортежи.


ЮЖ>>Здесь и до зависимых типов недалеко... Можно разрешить использование 'this' и в том числе для использования при проверки предусловий для аргументов + состояние объекта, т.е.

ЮЖ>>
ЮЖ>>void Container::Remove(Key k : !this.Empty && this.Exists(k))
ЮЖ>>

S>Ну вот — видишь, здесь никаких кортежей нет, а предусловия какбы есть.

Почему нет? кортеж (Key, Container) + оочень специфический "конструктор".

ЮЖ>>Можно пойти еще дальше: операция Remove принимает (как бы) тупл из двух элементов — (Container, Key):

ЮЖ>>
ЮЖ>>void Container::Remove(Container this_ : !this_.Empty, Key k : this_.Exists(k))
ЮЖ>>

S>Можно, а зачем?

Эта запись предназначена для объяснения того, как должен себя вести компилятор.


ЮЖ>>С 'Exists(k)' так просто не получится — но есть два варианта: самый простой — автоматически добавлять runtime проверку (Assert или что-то еще), второй — то же что и первый, + нужно исходить из того факта, что помимо совпадения произвольного ключа возможен случай с передачей в Remove значения, полученного от этого же экземпляра контейнера (или эквивалентного находящемуся в нем) — если между этими событиями модификация контейнера не производилась — есть 100% гарантия нахождения значения с указанным ключом в контейнере (можно анализировать и более сложные случаи). В этом случае проверка не нужна. Правда в этом случае компилятор должен поддерживать аннотирование любых объектов. Вобщем получаются predicate types.

S>Получится очень просто: либо компилятор статически может проверить гарантию наличия значения по ключу, либо пользователь обязан явно описать уточнение.
S>То есть вот такое примерно должно иметь место:
S>
S>public void Test(Dictionary<string, int> data)
S>{
S>  data["test"] = GetExternalInt(); // тип data неявно сменился
  
S>  int a = data["test"]; // компилятор пропускает, потому что там точно лежит int
  
S>  int b = data["fail"]; // компилятор ругается, потому что нет гарантии наличия ключа
  
S>  int? c = data["fail"]; // компилятор спокоен, потому что мы явно предусмотрели отсутствие ключа

S>  int d = 0;  
S>  if (data.Exists("fail")) // тип data неявно сменился 
S>    int d = data["fail"]; // компилятор спокоен, потому что в этой ветке есть гарантия наличия ключа
S>}
S>


Что-то типа этого. Теоритически можно заставить работать и такое:

string s = ...;
data[s]  = ...

Y y(s); // s 'ушла'
string s2 = y.bar(); // ... и вернулась, т.е. s == s2 

int n = data[s2]; // ok


Здесь в постусловии конструктора Y может присутствовать предикат вида this.bar() == аргумент_конструктора, + компилятор/анализатор должен быть уверен в том что содержимое data не изменялось во время 'путешествия' s, либо эти изменения не повлияли на нее. Правда в пределе может возникнуть ситуация, когда время компиляции стремится к бесконечности. Здесь логичнее плясать от статистики по выявленным дефектам и в первую очередь пытаться переложить на плечи компилятора работу по выявлению наиболее частых. Но это слабореализуемо...

ЮЖ>>Если же не рассматривать вышесказанное, то имхо, идея именования элементов туплов вполне жизнеспособна, но озвученные аналогии с именами для аргументов начинают немного хромать в присутствии аргументов по умолчанию (для полноты картины):

ЮЖ>>Но аналогия для возвращаемых значений тут неприменима. Да, можно игнорировать некоторые элементы, но активное использование такой 'фичи' больше похоже на симптомы проблем с дизайном.
S>Не вижу никакой хромоты.

Использовании значения по умолчанию — такие значения все же будут использованы для дальнейших вычислений. Неиспользование результата — скорее должно приводить к элиминации соответствующих вычислений, т.е. по сути — ленивые вычисления. Игнорирование здесь мало подходит, т.к. в этом случае не получится игнорировать возможные побочные эффекты, + возможные затраты на вычисление.
Re[26]: еще один пример
От: Pavel Dvorkin Россия  
Дата: 14.10.09 12:49
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Речь в нашем треде давно уже про это не идет. Речь идет о промежуточных переменных и вызове конструкторов для этого. И ты это прекрасно знаешь.

S>Нет, Павел. Не надо тут пытаться поймать меня на знании деталей С++. Я понимаю, что это твоя любимая тема, но обсуждаем мы не её.

Ловить тебя мне совсем не интересно, а про детали С++ (кроме того, как и можно ли вообще соотнести строчки С++ и команды), вроде речи вообще не было. Примеры мои — деталям не посвящены. И до сих пор тебя это не тревожило. Что так вдруг ?

S>Поясняю: я пытался объяснить суть происходящего на понятном тебе языке. Я не виноват, что ты стал приводить примеры именно на С++.


Да бога ради, мог привести на C# или Java. Я выбрал то, что мне ближе, возражений об языке не последовало.

S>И не я виноват, что именно ты привёл примеры, которые даже при компиляции на С++ подтверждают именно мою точку зрения.


Ай-яй-яй!
Ты же сам утверждаешь, что без профайлера ничего не докажешь. А мы даже и не компилировали ни один пример, более того, и не написали его как следует. Ты сам себе противоречишь — ты уже без всякого профайлинга доказал, что твой подход лучше. Я это мог бы себе позволить, но ты-то как ?

Вот то-то и оно. Психология. Типичная оговорка по Фрейду.

S>Тебе пришлось сильно постараться, чтобы придумать пример, который (как ты думаешь) заведомо не получится оптимизировать компилятором.


Я его придумывал секунд 30, пока ходил в курилку. Стараться тут нечего — пока домой доеду, могу еще много других придумать. Но существенен факт — ты все же признал, что не удастся ему оптимизировать. ну и это уже хорошо.

S>И футбол тоже не то, про что эта тема. Поэтому мы не будем ставить полезность возможности возврата нескольких значений от того, обсуждают ли назначение углового после ввода мяча от ворот.


Заиграно. Ели хочешь обсуждать опять туплы — перейди в подветку, где продолжают их обсуждать.

S>Тогда ты взялся за возврат туплов со стороны "оптимизации", причём как обычно — приводя примеры, которые опровергают твою же точку зрения.


То они опровергают, то все же не получится оптимизировать. Пойди пойми.

PD>>А, прекрасно. Значит, N раз все же писать не буду. Один раз напишу, тут же пойму, исправлюсь, и больше писать не буду.

PD>>Ответ — если соображу вовремя — напишу 0 раз. Сразу без промежуточной переменной. Если не соображу — написав, нажму несколько раз Del, BackSpace, Shift-Ctrl-L и поправлю. Тебе надо подробно описать, как именно я код редактирую ?
S>Нет, не надо. Всё понятно и так.

Слава богу

PD>>Хм... Да, если написать вариант с моими точками, то сможет. А вот в задаче с массивом-списком — я и сам могу сопоставить.

S>Напомни, пожалуйста, какое отношение к туплам имеет задача с массивом-списком? Ну или, хотя бы, какое отношение она имеет к неявным преобразованиям?

Не юли. Раньше надо было о туплах говорить. А то два дня уже обсуждаем совсем другое, а теперь вспомнил...

PD>>Без профайлера и даже без запуска. Потому как нечего из Новосибирска в Омск через Владивосток, Нью-Йорк и Москву лететь. Тут и так все ясно. И даже если этот полет ты совершишь один раз в жизни, и даже если тебе на деньги и время наплевать, то все равно кроме как глупостью этот полет никак назвать нельзя.

S>Но именно ты почему-то начал писать именно такой полёт. Посмотри в свой код — у тебя там явно сказано "лететь через Array". Подумай — зачем ты это написал? Как на самом деле выглядит код обращения такого списка — пусть такой, где никто не задумался об оптимизации заранее.

Нет. Это не я написал. Я такие вещи писать не буду. Это я продемонстрировал как любители неявных преобразований могут лишнюю бессмысленную работу делать. Иными словами, это я объяснил, как эти авторы летают иногда из Н-ска в Омск.


PD>>Не бред, а фальшь. Твоя. Вопрос об том, надо ли оптимизировать 1%, я поставил безотносительно от чего бы то ни было. Просто — надо или нет. И множества значений (туплы) здесь абсолютно ни при чем.

S>Ок, давай тогда я спрошу безотносительно всего: нужно ли снимать кожуру с яблока? А если не ответишь — будем считать, что все твои выводы в этой ветке неверны. Раз уж пошла такая пьянка, задавать не относящиеся к делу вопросы.

Раньше думать надо было.

PD>Оптимизировал я не свой код, а он был на С, так что не могло там быть неявных преобразований. Да и не все ли равно тебе, как я оптимизировал ? Ты лучше на вопрос ответь — нужно ли оптимизировать, если удастся выиграть 1% ? Опять пытаешься уйти от вопроса.

S>В большинстве случаев — не нужно. 1% — это типичная погрешность измерений. Допускаю, что где-то, в отдельных случаях, это может играть какую-то роль. Но опять же, получается, что полезность возврата одним махом множества значений теперь зависит только от того, надо ли оптимизировать 1%. Ну не бред ли?

Писал такое ? Не вторую, а первую фразу ? Писал. То есть согласился ее обсуждать. А теперь начал заявлять, что это, мол, к делу не оносится. Да, прямо не относится. Но раз уж так — надо было сразу ответить. А взявшись отвечать по существу — не говори, что не относится. Поезд ушел. Или самолет. Из Н-ска в Омск. Через Нью-Йорк
With best regards
Pavel Dvorkin
Re[28]: доведение ad absurdum
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 13:13
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Демагогия. Никто ничего зловредного не обсуждал.
Цитирую:

Linked2List reverseList = Linked2List(Array(sourceList));

Кто это написал? Я что ли? Весь смысл этой программы — не дать компилятору её соптимизировать.

S>>Ты можешь писать сколько угодно примеров идиотского кода, который занимается нагреванием воздуха.

PD>Демагогия.
Опять цитирую:

Linked2List reverseList = Linked2List(Array(sourceList));

Этот код занимается нагреванием воздуха.

S>>а) написать много лишнего кода, который всё равно выкинет компилятор

PD>Не было.
Цитирую:
IPoint ip;
GetPointByRef(ip);
FPoint fp1 = ip;
FPoint3D fp3D = fp1;

Весь лишний код будет устранён компилятором (если не предпринимать специальных усилий).
S>>б) написать много лишнего кода, который бы всё равно был выкинут компилятором, и выкинуть его вручную
PD>Не было. Я как раз предлагал его не писать.
Цитирую:

А вот если бы я написал

IPoint ip;
GetPointByRef(ip);
FPoint fp1 = ip;
FPoint3D fp3D = fp1;


то я бы сразу задал себе вопрос — зачем я тут ерундой занимаюсь!

IPoint ip;
GetPointByRef(ip);
FPoint3D fp3D = ip;

Самостоятельно написал лишнюю конверсию IPoint->FPoint->FPoint3d, самостоятельно её выбросил.

S>>в) написать много лишнего кода, который компилятор не сможет выкинуть

PD>Тоже ложь. Я как раз и предлагал его не писать.
Цитирую:
Linked2List reverseList = Linked2List(Array(sourceList));

Вот этот Array — он здесь зачем? Ясно: для доказательства того, что компилятор не может его выбросить. Здесь нет ни одного неявного преобразования, зато есть явная инструкция "делай медленно".
S>>г) написать много лишнего кода, который компилятор не сможет выкинуть, и выкинуть его вручную.
PD>и опять ложь.
Снова цитирую:

а их конкатенация применительно к данной задаче — абсурдна. Потому что задача решается намного проще. Без всякого выкладывания в массив.

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

PD>А правда лишь одна — я говорю, что при анализе кода в применении к вопросу о явных и неявных преобразованиях может выясниться, что кое-что делать совсем не надо, а поэтому не надо и писать. И это легче будет обнаружить при явных, нежели при неявных преобразованиях.

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

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

PD>А мне в ответ — все, что угодно. Ладно. Оставайся при своем мнении.

И вам того же.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 13:31
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Хм... Но это как раз ортогонально именам членов тупла при декларации функции.

Это — да. Неортогональна именам "членов тупла" возможность получить в IDE подсказку о том, что куда попадает.

А сама эта фича несколько противоречит, собственно, самому туплу — тут его, в общем-то, и нет. Есть пара destinations, куда должен попасть результат.
С точки зрения CLR, к примеру, мы можем считать, что оба значения просто оказались на стеке после вызова.
А потом мы уже одно из них употребили при помощи stloc, а другое — при помощи вызова сеттера.
Ну, а _, соответственно, означает просто pop в нужном месте. Но всё это, естественно, чисто императивная ересь. Я вообще как-то сомневаюсь в её полезности в контексте ФП. Поэтому я и написал, что с планами Влада это, скорее всего, будет расходиться.

ЮЖ>У меня присутствует ощущение того, что если постусловие в отношении функции для такого кортежа (особенно если его члены именованы) равное 'true' (т.е. вообще любой набор значений является допустимым), то это следствие нарушения SRP.

Возможно — не буду спорить. Хотя не очень понятно, SRP там или нет. А какой принцип будет нарушать вырожденное постусловие на скалярный результат? Ведь в природе тоже неограниченные контракты встречаются редко.
Механика контрактов — могучая вещь, но, повторюсь, она как-то выходит за пределы туплов.

ЮЖ>Это далеко не стопроцентный критерий, разумеется, но даже упомянутая выше функция IntDivide имеет постусловия в отношении результата 'в целом', т.е. фактически имеется связь между компонентами (в смысле cohesion).

Это правда. Но даже если мы рассмотрим скалярную функцию /, то её результат связан с аргументами.

ЮЖ>С другой стороны я вижу сценарии, когда инварианты на кортеж не являются значимыми в точке использования, но это будут скорее утилитарные вещи, для которых достаточно индекснего доступа для отдельных элементов. Но в этом случае такой синтаксис:

ЮЖ>
(double x, double y) = GetXY()

ЮЖ>не является необходимым, достаточно доступа к первому элементу и к списку оставшихся.
Может, это Лисп?
Я думаю, что могут быть сценарии, которые осознанно нарушают SRP ради получения каких-то других бенефитов.

ЮЖ>Что-то типа этого. Теоритически можно заставить работать и такое:


ЮЖ>Здесь в постусловии конструктора Y может присутствовать предикат вида this.bar() == аргумент_конструктора, + компилятор/анализатор должен быть уверен в том что содержимое data не изменялось во время 'путешествия' s, либо эти изменения не повлияли на нее. Правда в пределе может возникнуть ситуация, когда время компиляции стремится к бесконечности. Здесь логичнее плясать от статистики по выявленным дефектам и в первую очередь пытаться переложить на плечи компилятора работу по выявлению наиболее частых. Но это слабореализуемо...

Не вижу Проблемы. Статистика быстро подскажет нам, что самые популярные — это NullReferenceException и IndexOutOfRangeException.
Оба, очевидно, лечатся с помощью ЗТ. Причем первый — вообще тривиальным способом, т.к. там собственно зависимости нет; всё будет работать даже на существующем фреймворке — дайте только новый компилятор и разметьте атрибутами mscorlib. Со вторым поинтереснее, но по-прежнему у нас нет NP-задачи. Точнее, мы можем выделить относительно разрешимую подзадачу.
Для начала — научиться отличать заведомо пустые коллекции от заведомо непустых; OutOfRange в 90% случаев — это обращение по индексу 0.

ЮЖ>Использовании значения по умолчанию — такие значения все же будут использованы для дальнейших вычислений.

По хорошему, они тоже должны элиминироваться. Т.е. для значений по умолчанию компилятор может построить более эффективный код.
ЮЖ>Неиспользование результата — скорее должно приводить к элиминации соответствующих вычислений, т.е. по сути — ленивые вычисления.
Опять же — при инлайнинге возможности открываются поистине бесконечные. Либо, как минимум, опять можно специализировать код функции по способам применения — не обязательно прямо-таки для каждого call site делать отдельную версию. Возможных вариантов игнорирования N результатов всего 2^N . C учётом того, что всё это возложено на JIT, нас не сдерживает code bloat во время статической компиляции.
ЮЖ>Игнорирование здесь мало подходит, т.к. в этом случае не получится игнорировать возможные побочные эффекты, + возможные затраты на вычисление.
Это если побочные эффекты есть. У Липперта, помнится, есть упоминание про то, как оптимизируется код "вокруг" побочных эффектов.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 13:33
Оценка: +1
Здравствуйте, lomeo, Вы писали:


L>Как это — кортеж поддерживает структурную идентичность? Разве кортеж — это не отдельный тип?


Кортежи — да. Но для них можно просто описать набот дженерик-типов (так как имена полей у них фиксированные).
Сделать тоже самое с записями невозможно, так как у них разные имена полей.

L>
L>class Tuple2<A,B>
L>class Tuple3<A,B,C>
L>...
L>


L>В Scala — так, в Nemerle по другому?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[12]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 13:39
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


VD>>Тогда за фиг тебе С++? На С можно писать точно так же, только с большим количеством явно выраженных деталей. Ну, а раз можно обходиться, то нужно (по твоей логике)!


PD>Инкапсулирование. На С практически невозможно, разве что пародия.


Да, ну? Иди попробуй нарушить инкапсуляцию Win API.

PD>Наследование. То же.


Да, ну. Это просто сахар же. Реализуется вручную только дат.

PD>Полиморфизм. То же.


Та же фигня.

Ну, или тебе придется признать, что точно так же в языке нужны и иные расширения упрощающие жизнь.

Если ты еще не понял, то разговоры на тему зачем нужны кортежи ничем не отличаются от разговоров на тему зачем нужны классы. Просто ты предвзято относишься к проблеме.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 14.10.09 16:16
Оценка: 6 (1)
Здравствуйте, VladD2, Вы писали:

VD>Речь идет об включении в описание функции имен этих отдельных значений.

VD>А луа это динамически типизированный скрип в котором даже понятия такого как описание типа нет.

Стало интересно, как это может выглядеть на упомянутом выше netch80 питоне. Как-то так:

from inlinerecords import _, returnsnames

@returnsnames(ident.topicid, ident.title, ident.author)
def getmessage():
    return (3564395, 'Inline records', 'VladD2')

message = getmessage()

print message
print 'topicId=%s, title=\'%s\', author=\'%s\'' % (message.topicid, message.title, message.author)


выведет:

(3564395, 'Inline records', 'VladD2')
topicId=3564395, title='Inline records', author='VladD2'




код inlinerecords.py:

'''
Кортеж, хранящий свои элементы еще и в именованных полях.
Инстанцирование: x = inlinerecord({name1:value1, name2:value2 ..., nameN:valueN})
'''
class inlinerecord(tuple):
    def __new__(cls, dict):
        for name, value in dict.iteritems(): setattr(cls, name, value)
        return super(inlinerecord, cls).__new__(cls, dict.values())

        # все по-честному, мы также как и tuple будем неизменяемыми ;)
    def __setattr__(self, *args):
        raise TypeError("can't modify immutable instance")
    __delattr__ = __setattr__

'''
Класс-пустышка, в ответ на обращение к любому его несуществующему полю возвращает строку с именем этого поля:
print x = ident.test - выведет "test"
'''
class ident(object):
    class __metaclass__(type):
        def __getattr__(cls, attr): return attr

'''
декоратор для оборачивания функций, возвращающих кортежи с целью их замены на кортежи с именованными полями
'''
def returnsnames(*names):
    def wrapper(f):
        return lambda *args, **kwds: inlinerecord(dict(zip(names, f(*args, **kwds))))
    return wrapper

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 18:39
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Как это — кортеж поддерживает структурную идентичность? Разве кортеж — это не отдельный тип?


Смотря где. В реляционной модели запись определяется как кортеж с именованными полями, а тип записи определяется совокупностью типов ее полей и имен.

L>В Scala — так, в Nemerle по другому?


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

За скалу не скжу, но что-то не помню там синтаксиса для записи записей.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 18:46
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

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


VD>>Речь идет об включении в описание функции имен этих отдельных значений.

VD>>А луа это динамически типизированный скрип в котором даже понятия такого как описание типа нет.

KV>Стало интересно, как это может выглядеть на упомянутом выше netch80 питоне. Как-то так:...


Ну, так выходит, что речь идет не о типах, а о некоторой метапрограмме которая переписывает код.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 18:54
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

Кстати, можно пояснить (прямо по кускам разобрать и прокоментировать) код:

class inlinerecord(tuple):
    def __new__(cls, dict):
        for name, value in dict.iteritems(): setattr(cls, name, value)
        return super(inlinerecord, cls).__new__(cls, dict.values())


Как я понимаю в питоне уже есть тип record?
Именно он возвращается?

Ну, и поддерживается ли для типа record (если он существует) структурная идентичность? Ну, когда две независимо созданные записи идентичны при условии, что идентичны значения и имена полей.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 18:57
Оценка:
Здравствуйте, netch80, Вы писали:

N>Это не "докапывание до терминов", это поиск согласования. Когда ты говоришь какие-то вещи безусловно не соответствующие общепринятому пониманию,


Что не соответствует общепринятому пониманю? Что скрипты называют нетипизированными или динамически-типизированными? Или то, что все эти термины в обиходе означают одно и то же?

N>"совершенно точно имеет факт одного из двух" — или непонимание, или своя терминология ради какой-то своей цели. Но причины такого определения ты раскрывать не хочешь — ну что ж, заставить нельзя


Значит я правильно понял, что ты все же решился докопаться до термина "нетипизированный"?
Тебя устроить если вместо него использовать динамически-типизированный?

VD>> В данном случае совершенно точно имеет факт одного из двух.


N>Мимо. Впрочем, возобновлять дискуссию мне облом.


Что мимо то? Я так и не понял что ты хотел сказать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Inline records
От: Юрий Жмеренецкий ICQ 380412032
Дата: 14.10.09 19:46
Оценка:
Здравствуйте, Sinclair, Вы писали:

ЮЖ>>У меня присутствует ощущение того, что если постусловие в отношении функции для такого кортежа (особенно если его члены именованы) равное 'true' (т.е. вообще любой набор значений является допустимым), то это следствие нарушения SRP.

S>Возможно — не буду спорить. Хотя не очень понятно, SRP там или нет.

Что-то я не то сказал. Имел ввиду другое, но в дальнейших рассуждениях появились дополнительные факторы и они пока не совсем увязались между собой.

S>А какой принцип будет нарушать вырожденное постусловие на скалярный результат? Ведь в природе тоже неограниченные контракты встречаются редко.


Никакое не нарушают. А встречаются не так уж и редко — например функция которая возвращает bool =).

S>Механика контрактов — могучая вещь, но, повторюсь, она как-то выходит за пределы туплов.


Совсем без контрактов получится аналог голой структуры, про значения которой и про взаимосвязи между которыми ничего неизвестно. Вот это мне совсем не нравится. Добавление контрактов на аргументы/состояние/возвращаемое значение в объявление функции, имхо, лучше чем в определение (особенно для постусловий), и еще лучше чем в комментарии.

ЮЖ>>Это далеко не стопроцентный критерий, разумеется, но даже упомянутая выше функция IntDivide имеет постусловия в отношении результата 'в целом', т.е. фактически имеется связь между компонентами (в смысле cohesion).

S>Это правда. Но даже если мы рассмотрим скалярную функцию /, то её результат связан с аргументами.

Безусловно, но я имел ввиду наличие как минимум двух значений, которые возвращаются в качестве результата и взаимоотношениях между ними. Впрочем, к туплам, как таковым это не относится.

ЮЖ>>С другой стороны я вижу сценарии, когда инварианты на кортеж не являются значимыми в точке использования, но это будут скорее утилитарные вещи, для которых достаточно индекснего доступа для отдельных элементов. Но в этом случае такой синтаксис:

ЮЖ>>
(double x, double y) = GetXY()

ЮЖ>>не является необходимым, достаточно доступа к первому элементу и к списку оставшихся.
S>Может, это Лисп?

Пусть будет С++, там возможность получить тупл со ссылками на все члены объекта / параметры функции весьма кстати.


S> Статистика быстро подскажет нам, что самые популярные — это NullReferenceException и IndexOutOfRangeException.


Это не дефекты — это симптомы. Дефект может быть совсем в другом месте.

S>Оба, очевидно, лечатся с помощью ЗТ. Причем первый — вообще тривиальным способом, т.к. там собственно зависимости нет; всё будет работать даже на существующем фреймворке — дайте только новый компилятор и разметьте атрибутами mscorlib. Со вторым поинтереснее, но по-прежнему у нас нет NP-задачи. Точнее, мы можем выделить относительно разрешимую подзадачу.


Все рано или поздно упрется в определение эквивалентности двух объектов. Например: берем строку — литерал в коде, состоящий из K символов (известных). шифруем ее каким-нибудь алгоритмом, сжимаем ее, конкатенируем саму с собой, удаляем все буквы 'x', вобщем в том же духе, потом берем два символа с начала строки и два с конца — конкатенируем... а дальше строка 'if (s == "2009")'. Вот здесь компилятор особенно обрадуется... Или тоже самое в присутствии внешних данных и возможных дефектов. Строку можно заменить на какой-нибудь граф объектов...

ЮЖ>>Использовании значения по умолчанию — такие значения все же будут использованы для дальнейших вычислений.

S>По хорошему, они тоже должны элиминироваться.

Как элиминироваться? От них же (значений) код зависит.

string f(string name, string prefix = "Dear "){
  return prefix + name;
}
//...
f("Bob!");


В частном случае их использование может зависеть от других значений...

ЮЖ>>Игнорирование здесь мало подходит, т.к. в этом случае не получится игнорировать возможные побочные эффекты, + возможные затраты на вычисление.

S>Это если побочные эффекты есть. У Липперта, помнится, есть упоминание про то, как оптимизируется код "вокруг" побочных эффектов.

Можно взять эффекты такого рода:
int foo()
{
  int x = f();
  int y = g(); 
  if(y == 100)
  {
    x++;
  }

  return (x, y); // y - не будет использоваться.
}


Вот такой случай я выше имел ввиду под "зависимыми значениями" результата.
Re[11]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 14.10.09 20:50
Оценка:
Здравствуйте, VladD2, Вы писали:

N>>Это не "докапывание до терминов", это поиск согласования. Когда ты говоришь какие-то вещи безусловно не соответствующие общепринятому пониманию,

VD>Что не соответствует общепринятому пониманю? Что скрипты называют нетипизированными или динамически-типизированными? Или то, что все эти термины в обиходе означают одно и то же?

Второе. Такой "обиход" соответствует разве что традиции твоего кружка. Остальные знают, что есть статическая типизация и динамическая, строгая и нестрогая, основанная на наследовании и утиная, в каждой паре масса промежуточных вариантов, комбинируются они тоже не всегда тривиально. И наконец, что обобщать как "скрипты" группу совершенно по-разному устроенных языков — нелепица. И что твоё обобщение почему-то включило в себя ещё те языки, которые ничуть не "скрипты" — тот же Erlang, который компилируется, но в котором статическая оптимизация есть сейчас только в маргинальных случаях.

N>>"совершенно точно имеет факт одного из двух" — или непонимание, или своя терминология ради какой-то своей цели. Но причины такого определения ты раскрывать не хочешь — ну что ж, заставить нельзя:))

VD>Значит я правильно понял, что ты все же решился докопаться до термина "нетипизированный"?
VD>Тебя устроить если вместо него использовать динамически-типизированный?

В локальном смысле — да, если принять такое определение для ветки дискуссии. В глобальном — уже нет. Потому что ты
1) из всех вопросов, которые обсуждались, отрезал и выпятил только один
2) развил его в агрессивно-парадоксальном стиле, с переходом на личности ("решился докопаться" и тому подобные обращения)
3) при этом пытаешься сместить терминологию в сторону, не обусловленную ни одним объективным фактором

И, собственно, кто тут должен утверждать "не могу вести дискуссию в таком стиле" — вопрос спорный.

VD>>> В данном случае совершенно точно имеет факт одного из двух.

N>>Мимо. Впрочем, возобновлять дискуссию мне облом.
VD>Что мимо то? Я так и не понял что ты хотел сказать.

Что "имеет факт одного из двух". Имел факт вопроса, на основании чего такое странное утверждение — может, ты пытался выдвинуть какую-то совершенно новую идею. Но вместо этого я увидел только волну обструктива, а это действительно облом продолжать.
The God is real, unless declared integer.
Re[13]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 14.10.09 20:54
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>>>Тогда за фиг тебе С++? На С можно писать точно так же, только с большим количеством явно выраженных деталей. Ну, а раз можно обходиться, то нужно (по твоей логике)!

PD>>Инкапсулирование. На С практически невозможно, разве что пародия.
VD>Да, ну? Иди попробуй нарушить инкапсуляцию Win API.

Если формально подходить к вопросу — это более чем возможно. Очень многие вещи в WinAPI реализуются в userland'е использующего процесса — например, к этому относится значительная часть GUI, и залезть в область данных рисуемого приложением окна (при том, что адрес области добывается через GetWindowLong) — столь же легко, как запороть любые другие данные.

Сложно доступиться только до того, что находится в пространстве ядра или другой задачи. Но это сложно уже на любом языке, в котором нет аналога LA 1,12 / SVC 107 :)) и тут аналогично C и C++ не помогут.

Вообще, ваш спор существенно ни о чём.

VD>Если ты еще не понял, то разговоры на тему зачем нужны кортежи ничем не отличаются от разговоров на тему зачем нужны классы. Просто ты предвзято относишься к проблеме.


Вот тут +100. Наличие механизма, упрощающего написание и понимание типичных конструкций, всегда полезно.
The God is real, unless declared integer.
Re[4]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 14.10.09 21:02
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

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


VD>>Речь идет об включении в описание функции имен этих отдельных значений.

VD>>А луа это динамически типизированный скрип в котором даже понятия такого как описание типа нет.

KV>Стало интересно, как это может выглядеть на упомянутом выше netch80 питоне. Как-то так:


Между прочим, в стандартной библиотеке подобные "примочки" уже есть. А именно: os.stat(), os.lstat() возвращают объект, который при обращении к нему как sequence ведёт себя как кортеж на 10 значений, а как к объекту — выдаёт их как поля. (В отличие от Вашего кода, его генерация сделана на C, так что показательный результат не будет.)
The God is real, unless declared integer.
Re[5]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 15.10.09 00:05
Оценка:
Здравствуйте, VladD2, Вы писали:

KV>>Стало интересно, как это может выглядеть на упомянутом выше netch80 питоне. Как-то так:...


VD>Ну, так выходит, что речь идет не о типах, а о некоторой метапрограмме которая переписывает код.


Не-не, это именно честный тип, просто он конструируется в python-way стиле. В том смысле, что если это выглядит как тип, ведет себя как тип и используется как тип, то это тип

Я так понимаю, тебя смутили две вещи:

1. упоминание метакласса в определении ident — этот класс вообще можно смело выкинуть, он используется только для того, чтобы декларировать имена элементов кортежа не строкой, а выражением типа ident.<имя_элемента>. Т.е. если его вообще убрать, то единственное что изменится, это список аргументов у декоратора returnsnames, он станет таким:

@returnsnames('topicid', 'title', 'author')
def getmessage():
    return (3564395, 'Inline records', 'VladD2')


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

2. то, что переменные экземпляра класса со значениями элементов кортежа добавляются в экземпляр "на лету". Но это и есть pythonic-way создания экземплярных переменных — только через их инициализацию. Т.е. если нам нужен класс А, экземпляр которого будет иметь два публичных поля x и y, то единственный способ обеспечить это, такой:

class A:
    def __init__(self):
        self.x = 0
        self.y = 'bla-bla-bla'


Где __init__() — инициализатор экземпляра, который дергается автоматически интерпретатором срауз после инстанцирования класса.

Т.е. нет возможности, как в статических языках декларировать наличие экземплярных полей в описании класса. Зато, в силу строгости типизации, мы после этого не сможем сделать, например, вот так:

a = A()
a.x += a.y


т.к. в инициализаторе для A().x уже был выведен тип int, оператор сложения которого не умеет принимать строку в качестве правого аргумента, для A().y был выведен тип str, а неявные преобразования типов — это php-way, а не питона.

Если интересно, вот тут
Автор: kochetkov.vladimir
Дата: 13.10.09
я набросал тот интерпретатор арифметических выражений из твоей статьи. Там динамизм питона используется в полный рост

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[5]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 15.10.09 00:05
Оценка: 59 (2)
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, kochetkov.vladimir, Вы писали:

VD>Кстати, можно пояснить (прямо по кускам разобрать и прокоментировать) код:

Да, конечно. Правда в процитированном тобой коде я дважды ошибся (один раз — фатально ), поэтому ниже — немного исправленный/дополненный вариант и целиком, ок?

Определяем новый тип inlinerecord, наследуемый от стандартного типа tuple (простой кортеж):
class inlinerecord(tuple):


__new__ — это конструктор экземпляра класса, он дергается интерпретатором автоматически, при каждом инстанцировании. В cls — передается сссылка на инстанцируемый класс (подставляется автоматически, интерпретатором), dict- обычный аргумент конструктора класса:
    def __new__(cls, dict):


Находим своего родителя и дергаем его конструктор для получения ссылки на вновь созданный экземпляр:
        instance = super(inlinerecord, cls).__new__(cls, dict.values())


Сохраняем имена полей записи из словаря в экземплярную переменную names. instance.__dict__ — это внутренний словарь пространства имен нашего экземпляра, т.е. это то же можно было записать как instance.names = tuple(dict.keys()), но в данном случае так нельзя. Почему — см. ниже:
        instance.__dict__['names'] = tuple(dict.keys())


Перебираем все пары значений из словаря с именами/значениями, разворачивая их в name и value и для каждой пары создаем экземплярную переменную ссоответствующим именем и значением:
        for name, value in dict.iteritems(): instance.__dict__[name] = value


Возвращаем созданный экземпляр класса inlinerecord (это ответ на один из твоих вопросов):
        return instance


__setattr__ автоматически дергается интерпретатором при попытке изменить или создать атрибут экземпляра. Выбрасывая тут исключение, мы тем самым запрещаем какую-либо модификацию нашего экземпляра. Именно поэтому в конструкторе приходится использовать прямую запись в пространство имен, а не кошерный способ:
    def __setattr__(self, *args):
        raise TypeError("can't modify immutable instance")


То же, но с удалением атрибутов:
    __delattr__ = __setattr__


__eq__ дергается интерпретатором когда наш экземпляр сравнивается (мы переопределили оператор "==", по сути) с другим объектом на предмет равенства. Возвращаем истину если их типы совпадают и у них одинаковые пары "имя:значение", в противном случае, возвращаем ложь:
    def __eq__(self, other):
        return False if type(other) is not type(self) else self.names == other.names and super(inlinerecord, self).__eq__(other)


VD>Как я понимаю в питоне уже есть тип record?

VD>Именно он возвращается?

Нет, по крайне мере, в стандартной библиотеке record'а нет. В коде возвращался созданный экземпляр класса inlinerecord.

VD>Ну, и поддерживается ли для типа record (если он существует) структурная идентичность? Ну, когда две независимо созданные записи идентичны при условии, что идентичны значения и имена полей.


Во втором варианте кода — да, поддерживается:

a = inlinerecord({'a': 0, 'b': 1})
b = inlinerecord({'a': 0, 'b': 1})
c = inlinerecord({'c': 0, 'b': 1})

print a.a, a.b
print b.a, b.b
print c.c, c.b

print a == b
print b == c
print a == c


Получаем

0 1
0 1

0 1
True
False
False


[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[6]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 15.10.09 00:10
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

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


VD>>Здравствуйте, kochetkov.vladimir, Вы писали:

VD>>Кстати, можно пояснить (прямо по кускам разобрать и прокоментировать) код:

KV>Да, конечно. Правда в процитированном тобой коде я дважды ошибся (один раз — фатально ), поэтому ниже — немного исправленный/дополненный вариант и целиком, ок?


И он же без комментов:
class inlinerecord(tuple):
    def __new__(cls, dict):
        instance = super(inlinerecord, cls).__new__(cls, dict.values())
        instance.__dict__['names'] = tuple(dict.keys())
        for name, value in dict.iteritems(): instance.__dict__[name] = value
        return instance

    def __setattr__(self, *args):
        raise TypeError("can't modify immutable instance")
    __delattr__ = __setattr__

    def __eq__(self, other):
        return False if type(other) is not type(self) else self.names == other.names and super(inlinerecord, self).__eq__(other)

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[13]: Inline records
От: Pavel Dvorkin Россия  
Дата: 15.10.09 02:05
Оценка: -1
Здравствуйте, VladD2, Вы писали:


VD>>>Тогда за фиг тебе С++? На С можно писать точно так же, только с большим количеством явно выраженных деталей. Ну, а раз можно обходиться, то нужно (по твоей логике)!


PD>>Инкапсулирование. На С практически невозможно, разве что пародия.


VD>Да, ну? Иди попробуй нарушить инкапсуляцию Win API.


Что за чепуха ? Ты спросил — зачем мне С++, почему не остаться на С. Я ответил — в С++ есть инкапсуляция, а в С нет. При чем тут WinAPI ?

PD>>Наследование. То же.


VD>Да, ну. Это просто сахар же. Реализуется вручную только дат.


Во-первых, я последнюю фразу не понял.

Во-вторых, напиши, как реализуется.

Вот тебе

struct POINT {
int x, y;
};

и сделай мне трехмерную точку. Из нее. Без наследования, на чистом С. А я попробую откомпилировать.


PD>>Полиморфизм. То же.


VD>Та же фигня.


Ну-ну. Реализуй, пожалуйста, вручную dynamic_cast на чистом С, сначала, конечно, сделай все же наследование.

VD>Ну, или тебе придется признать, что точно так же в языке нужны и иные расширения упрощающие жизнь.


Разница очень простая. Я говорю о том, без чего практически нельзя обойтись, а ты о том, что почти целиком и так есть, только малость по-другому сделано.

VD>Если ты еще не понял, то разговоры на тему зачем нужны кортежи ничем не отличаются от разговоров на тему зачем нужны классы. Просто ты предвзято относишься к проблеме.


Нет. Классы добавляют мне то, на что я потрачу десятки часов , чтобы реализовать вручную. А возврат кортежа — я могу на 99.9% сделать то же самое через выходные параметры
With best regards
Pavel Dvorkin
Re[29]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 15.10.09 02:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Pavel Dvorkin, Вы писали:

PD>>Демагогия. Никто ничего зловредного не обсуждал.
S>Цитирую:
S>

Linked2List reverseList = Linked2List(Array(sourceList));

S>Кто это написал? Я что ли? Весь смысл этой программы — не дать компилятору её соптимизировать.

Еще раз для не понимающих или не желающих понять. Это — демонстрация того, что можно сделать с твоим подходом.

S>>>Ты можешь писать сколько угодно примеров идиотского кода, который занимается нагреванием воздуха.

PD>>Демагогия.
S>Опять цитирую:
S>

Linked2List reverseList = Linked2List(Array(sourceList));

S>Этот код занимается нагреванием воздуха.

Опять демагогия. Свое дело он делает, то есть переворачивает список.

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

S>>>а) написать много лишнего кода, который всё равно выкинет компилятор

PD>>Не было.
S>Цитирую:
S>
S>IPoint ip;
S>GetPointByRef(ip);
S>FPoint fp1 = ip;
S>FPoint3D fp3D = fp1;
S>

S>Весь лишний код будет устранён компилятором (если не предпринимать специальных усилий).

Опять ложь. Здесь два конструктора. И будет ли их код устранен или нет — зависит от того, что там есть.


S>>>б) написать много лишнего кода, который бы всё равно был выкинут компилятором, и выкинуть его вручную

PD>>Не было. Я как раз предлагал его не писать.
S>Цитирую:
S>

А вот если бы я написал

S>

S>IPoint ip;
S>GetPointByRef(ip);
S>FPoint fp1 = ip;
S>FPoint3D fp3D = fp1;
S>


S>то я бы сразу задал себе вопрос — зачем я тут ерундой занимаюсь!

S>
S>IPoint ip;
S>GetPointByRef(ip);
S>FPoint3D fp3D = ip;
S>

S>Самостоятельно написал лишнюю конверсию IPoint->FPoint->FPoint3d, самостоятельно её выбросил.

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

S>>>в) написать много лишнего кода, который компилятор не сможет выкинуть

PD>>Тоже ложь. Я как раз и предлагал его не писать.
S>Цитирую:
S>
S>Linked2List reverseList = Linked2List(Array(sourceList));
S>

S>Вот этот Array — он здесь зачем? Ясно: для доказательства того, что компилятор не может его выбросить. Здесь нет ни одного неявного преобразования, зато есть явная инструкция "делай медленно".

Ну и ну! Чего-чего, а уж этого я от тебя не ждал.

Здесь вообще никаких инструкций типа "делай медленно" компилятору нет. Инструкции есть в конструкторах (или других методах), а здесь мы видим лишь вызовы их. Доказывается это совершенно элементарно :

Откуда тебе известно, что Array — это массив ? Ты этот вывод из строки

Linked2List reverseList = Linked2List(Array(sourceList));

сделал ? А на каком основании ? Потому что имя класса Array ?

А вдруг этот класс Array вовсе никакого массива и не содержит ? А содержит он только указатель на Linked2List. Конструктор Array по Linked2List просто этот указатель к себе записывает, а конструктор Linked2List по Array переворачивает входной список из Array и берет его к себе! И никакого массива вовсе нет!!! И будет быстро.

Ну и как ?

S>>>г) написать много лишнего кода, который компилятор не сможет выкинуть, и выкинуть его вручную.

PD>>и опять ложь.
S>Снова цитирую:
S>

S>а их конкатенация применительно к данной задаче — абсурдна. Потому что задача решается намного проще. Без всякого выкладывания в массив.

S>То есть сам написал выкладывание в массив, и сам же талантливо от него избавился. Браво, аплодирую стоя.

Аплодируй. Тебе больше ничего и не остается

PD>>А правда лишь одна — я говорю, что при анализе кода в применении к вопросу о явных и неявных преобразованиях может выясниться, что кое-что делать совсем не надо, а поэтому не надо и писать. И это легче будет обнаружить при явных, нежели при неявных преобразованиях.

S>Увы, никаких аргументов в пользу именно этого утверждения приведено не было. Ни один из примеров не содержит проблемы с неявным преобразованием, которую имеет смысл решать. Даже самый абсурдный пример построен на наличии явного преобразования, которое явно занимается ерундой.

S>В реальной жизни оказывается, что неявные преобразования порождают проблемы намного реже, чем решают. Потенциальные проблемы производительности при неявных преобразованиях

S>а) редкость (мы всё ещё ждем от мега-гуру плюсов убедительного примера )

Да уж какие еще тебе примеры приводить. Хватит и тех, что были.
With best regards
Pavel Dvorkin
Re[14]: Inline records
От: Pavel Dvorkin Россия  
Дата: 15.10.09 03:28
Оценка:
Здравствуйте, netch80, Вы писали:

N>Вот тут +100. Наличие механизма, упрощающего написание и понимание типичных конструкций, всегда полезно.


При условии. что при этом не нарушается бритва Оккама.
With best regards
Pavel Dvorkin
Re[15]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 15.10.09 06:04
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


N>>Вот тут +100. Наличие механизма, упрощающего написание и понимание типичных конструкций, всегда полезно.


PD>При условии. что при этом не нарушается бритва Оккама.


А вот с этим надо осторожнее. Потому что бритвой Оккама можно слишком многое отрезать. Как тебе вариант, когда операция вычитания отменена потому, что есть инверсия знака и вместо a-b можно всегда написать a+(-b)? И почему ни один распространённый язык программирования такого не требует — даже Форт, где для такой операции не нужны скобки?

Или — зачем нужны многомерные массивы? Всё ведь можно не хуже сделать в одномерных:))

Размахивая бритвой Оккама направо и налево, надо понимать, для каких проблем и каких ситуаций она придумывалась. Оккам в первую очередь возражал тем философам, которые придумывали тонны концепций только ради того, чтобы написать "красиво" с их точки зрения. Сейчас мало кто будет такое делать в программировании. В нём важно другое: вводить то, что уже доказано как полезное, и при этом оцененное как не вводящее диверсию против будущего развития — тут просмотр наперёд (насколько он вообще возможен) максимально важен.

Поэтому, если возражать против предложения, начавшего эту дискуссию — то только на основании противоречивости будущим направлениям развития языка. И я тут на 101% полагаюсь на VladD2 — он по-любому сможет оценить этот фактор лучше прочих участников треда.

А в общем случае — да, можно и Оккамом помахать:) вместо веера:))
The God is real, unless declared integer.
Re[14]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 15.10.09 06:29
Оценка: +3
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Вот тебе


PD>struct POINT {

PD> int x, y;
PD>};

PD>и сделай мне трехмерную точку. Из нее. Без наследования, на чистом С. А я попробую откомпилировать.


struct POINT3D {
  struct POINT plain;
  int z;
};

В документации пишем, что к элементам надо обращаться не x, y, z, а plain.x, plain.y, z.

Quod erat demonstrandum.

Этот приём систематически применяется, например, в GTK и GLib. Там сделана иерархия структур "объектов" интерфейса (местами до 5 уровней!) вложением структур. Для оптимизации доступа вглубь — используется конверсия указателей.

VD>>Если ты еще не понял, то разговоры на тему зачем нужны кортежи ничем не отличаются от разговоров на тему зачем нужны классы. Просто ты предвзято относишься к проблеме.

PD>Нет. Классы добавляют мне то, на что я потрачу десятки часов , чтобы реализовать вручную. А возврат кортежа — я могу на 99.9% сделать то же самое через выходные параметры

Сравнение некорректно. Ты используешь возможности (такие, как инкапсуляция, наследование...) потому что механизм уже есть. Кто-то уже потратил на это миллионы часов, при этом он сам по себе ничего Оккамом не рубил.

Разница между кортежом и выходными параметрами именно та, что первое даёт _функциональный_ подход. У тебя есть вход и выход из функции. В случае выходных параметров, если ты им не даёшь предыдущего значения — это ничем не отличается от кортежа, кроме синтаксического неудобства; а если у них уже было значение — это является разрушающим присвоением, который в этом подходе недопустим.
The God is real, unless declared integer.
Re[16]: Inline records
От: Pavel Dvorkin Россия  
Дата: 15.10.09 06:38
Оценка:
Здравствуйте, netch80, Вы писали:

N>Поэтому, если возражать против предложения, начавшего эту дискуссию — то только на основании противоречивости будущим направлениям развития языка.


С многим можно согласиться. Но вот с этим готов поспорить.

Допустим, VladD2 или кто-то еще оценил непротиворечивость будущему направлению развития и вынес вердикт — не противоречит. Вердикт этот под сомнение ставить не будем, примем как истину. Означает ли это, что можно добавлять ?

ИМХО нет. Язык ведь можно и по принципу языка-оболочки построить. Классический пример — PL/1. Он так и был задуман — взять все хорошее, что есть в Фортране, добавить все хорошее, что есть в Алголе, добавить все хорошее, что есть в Коболе (я не шучу, это именно так и было), получим универсальный язык, который может заменить и Алгол, и Фортран, и Кобол. И это не противоречит будущим направлениям. Понадобилось бы — он и C вместе с C++ в себя бы включил.

На первый взгляд ничего плохого. Сложим в кучу все, что есть, а потом пусть каждый программист выбирает из него то, что ему надо. Но не пошла эта концепция. Размывается при этом структура языка. Из чего-то, что имеет некую внутреннюю логику и построено в соответствии с некоей исходной идеей, язык превращается просто в набор всяких и разных фич, введенных не потому, что без них не обойдешься, а потому, что эта новая фича удобна и красива, хотя на 99% и прежними средствами можно было бы сделать то же самое.

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

Вот поэтому я за то, чтобы проверять бритвой все нововведения. Конечно, не надо доводить до абсурда. Но все же, если некое нововвдение таково, что оно на 99% может быть реализовано уже имеющимися средствами, то надо 10 раз подумать, стоит ли его добавлять.
With best regards
Pavel Dvorkin
Re[15]: Inline records
От: Pavel Dvorkin Россия  
Дата: 15.10.09 06:52
Оценка:
Здравствуйте, netch80, Вы писали:


PD>>и сделай мне трехмерную точку. Из нее. Без наследования, на чистом С. А я попробую откомпилировать.


N>
N>struct POINT3D {
N>  struct POINT plain;
N>  int z;
N>};
N>


Неужели ты думаешь, что я до такого сам не мог догадаться ?
Только не наследование это в классическом смысле, а наследование включением. Это совсем разные понятия.

N>В документации пишем, что к элементам надо обращаться не x, y, z, а plain.x, plain.y, z.


Замечательная мысль. Поитом еще одного наследника сделаем

struct EinsteinPoint {
struct POINT3D point3D;
int time;
};

и в инструкции напишем, что поля называются point3D.plain.x, point3D.plain.y, point3D.z и почему-то просто time;



Но и не это главное. Как методы наследовать-то будешь ? Пусть невиртуальные, ладно, но как наследовать-то ? В классе POINT3D будешь заново описывать все методы и реализовать их путем вызова соответствующего метода внедренного объекта ? Такое, конечно, имеет право на существование, и применяется, но... хорошенькое наследование, когда я обязан привести заново все методы базового класса!


N>Quod erat demonstrandum.


N>Сравнение некорректно. Ты используешь возможности (такие, как инкапсуляция, наследование...) потому что механизм уже есть. Кто-то уже потратил на это миллионы часов, при этом он сам по себе ничего Оккамом не рубил.


Ничего подобного. Если оставить в стороне виртуальность, то наследование не есть никакой механизм. Это просто требование к компилятору собрать воедино все поля (родителя и мои) и взять из описания родителя его функции и добавить их мне. Вот и все. Но вот это-то я и не могу компилятору С объяснить!
With best regards
Pavel Dvorkin
Re: Inline records
От: Undying Россия  
Дата: 15.10.09 06:53
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>А почему бы не рассматривать возвращаемое значение функции так же как набор параметров? При этом синтаксис может быть совершенно разрообразным.

VD>
VD>(string ret1, int ret2) FuncName(string param1, int param2)
VD>


Вообще-то это все полумеры. Т.к. если нам нужно вернуть что-то более сложное, чем набор параметров (например, ключ, что требует перекрытия Equals и GetHashCode), то придется по-прежнему для достижения читабельности писать гору кода, вроде:

class TourKey
{
  public readonly string Name;
  public readonly DateTime BeginTime;
  public TourKey(string name, DateTime beginTime)
  {
    this.Name = name;
    this.BeginTime = beginTime;
  }
  public override bool Equals(object obj)
  {
    ...
  }
  public override int GetHashCode()
  {
    ...
  } 
}

TourKey GetTourKey(Tour tour)
{
  ...
}


Вместо всего этого кошмара хотелось бы такой записи:

class TourKey implement PairedKey<string, DateTime, First as Name, Second as BeginTime>;

TourKey GetTourKey(Tour tour)
{
  ...
}


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

Причем все это достигается за пять копеек, без какой либо переделки CLR, достаточно прекомпилятору строку:

class TourKey implement PairedKey<string, DateTime, First as Name, Second as BeginTime>;


заменять на:

class TourKey
{
  readonly PairedKey<string, DateTime> obj;

  public TourKey(string Name, DateTime BeginTime)
  {
    obj = new PairedKey<string, DateTime>(Name, BeginTime);
  }

  public string Name
  {
    get { return obj.First; }
  }

  public DateTime BeginTime
  {
    get { return obj.Second; }
  }

  public override int GetHashCode()
  {
    return obj.GetHashCode();
  }

  //Имплементация остальных методов PairedKey
}


Также достоинством является, что это решение не требует изменения концепций языка, оно полностью находится в русле понятий C# 2.0 Т.е. такое решение язык усложняет минимально, а новых возможностей добавляет массу.
Re[16]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 15.10.09 07:12
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Неужели ты думаешь, что я до такого сам не мог догадаться ? :-)


При чём тут "мог догадаться"?

PD>Только не наследование это в классическом смысле, а наследование включением. Это совсем разные понятия.


На Си наследование проще всего реализовать через наследование включением.

N>>В документации пишем, что к элементам надо обращаться не x, y, z, а plain.x, plain.y, z.

PD>Замечательная мысль. Поитом еще одного наследника сделаем

PD>struct EinsteinPoint {

PD> struct POINT3D point3D;
PD> int time;
PD>};

PD>и в инструкции напишем, что поля называются point3D.plain.x, point3D.plain.y, point3D.z и почему-то просто time;


Именно! Но согласись, что всё это просто неудобства, а не принципиальная проблема.

PD>:-)

PD>Но и не это главное. Как методы наследовать-то будешь ? Пусть невиртуальные, ладно, но как наследовать-то ? В классе POINT3D будешь заново описывать все методы и реализовать их путем вызова соответствующего метода внедренного объекта ?

Зачем?

struct POINT* p = (struct POINT*) p3d;
return p->getX(p);


PD> Такое, конечно, имеет право на существование, и применяется, но... хорошенькое наследование, когда я обязан привести заново все методы базового класса!


Никто от тебя не требует их "привести заново", не выдумывай:)

N>>Quod erat demonstrandum.

N>>Сравнение некорректно. Ты используешь возможности (такие, как инкапсуляция, наследование...) потому что механизм уже есть. Кто-то уже потратил на это миллионы часов, при этом он сам по себе ничего Оккамом не рубил.
PD>Ничего подобного. Если оставить в стороне виртуальность, то наследование не есть никакой механизм. Это просто требование к компилятору собрать воедино все поля (родителя и мои) и взять из описания родителя его функции и добавить их мне. Вот и все. Но вот это-то я и не могу компилятору С объяснить!

Нет, это не наследование. Это реализация наследования в некоторых распространённых языках вроде C++. Да, типичная — когда определённые в предке имена включаются в плоское пространство имён потомка. Но не единственная, и не требуемая напрямую определением.
The God is real, unless declared integer.
Re[17]: Inline records
От: Pavel Dvorkin Россия  
Дата: 15.10.09 07:31
Оценка:
Здравствуйте, netch80, Вы писали:

N>На Си наследование проще всего реализовать через наследование включением.


Вот поэтому мне и нужен С++, чтобы не заниматься этим включением, а писать как в С++.


N>Именно! Но согласись, что всё это просто неудобства, а не принципиальная проблема.


Не соглашусь.

PD>>

PD>>Но и не это главное. Как методы наследовать-то будешь ? Пусть невиртуальные, ладно, но как наследовать-то ? В классе POINT3D будешь заново описывать все методы и реализовать их путем вызова соответствующего метода внедренного объекта ?

N>Зачем?


N>
N>struct POINT* p = (struct POINT*) p3d;
N>return p->getX(p);
N>


??? Что это за getX, вызываемая по указателю на POINT ??? На чистом С ?

Вот так еще можно

struct POINT* p = (struct POINT*) p3d;
return getX(p); // предполагаю, что есть getX(POINT*);


Но это для первого элемента структуры пройдет. А для следующего ? Будем sizeof'ы добавлять ? А выравнивание как ?

N>Никто от тебя не требует их "привести заново", не выдумывай


OK.

struct POINT {int x,y;};
struct TIME {int hour, minute, second};

// функции для работы с POINT — 10 штук
// функции для работы с TIME — 10 штук

struct TEXT_POINT_TIME
{
char mark;
char* str;
POINT pt;
TIME tm;
};

// функции для работы с mark, str я сам напишу, а вот для работы с полями pt и tm — новые создавать не буду. Скажи, как использовать старые.

TEXT_POINT_TIME tpt;
// и вот здесь хочу вызвать getX(POINT*)

N>Нет, это не наследование. Это реализация наследования в некоторых распространённых языках вроде C++. Да, типичная — когда определённые в предке имена включаются в плоское пространство имён потомка. Но не единственная, и не требуемая напрямую определением.


Вот с этим согласен. Не единственная. Есть и другие варианты, в том числе тот, о котором ты пишешь. Но я-то хочу перейти с С на С++ именно для этого
With best regards
Pavel Dvorkin
Re[5]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 15.10.09 07:38
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>За скалу не скжу, но что-то не помню там синтаксиса для записи записей.


Забей, я про таплы говорил, а не записи.
Re[18]: уточнение
От: Pavel Dvorkin Россия  
Дата: 15.10.09 09:47
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:


PD>Вот так еще можно


PD>struct POINT* p = (struct POINT*) p3d;

PD>return getX(p); // предполагаю, что есть getX(POINT*);


PD>Но это для первого элемента структуры пройдет. А для следующего ? Будем sizeof'ы добавлять ? А выравнивание как ?


Вообще-то можно, конечно. Есть такой макрос — offsetof. С его помощью можно получить смещение для любого поля структуры, при любом выравнивании. Так что теоретически задача решается. И даже для функций решается. Но упаси меня бог от такого программирования — это в корне противоречит всем принципам. Я уж не говорю, что в С нет никаких private — protected и придется напрямую копаться в структурах без всякой защиты. Нет уж, только без меня
With best regards
Pavel Dvorkin
Re[17]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 15.10.09 10:02
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

N>>Поэтому, если возражать против предложения, начавшего эту дискуссию — то только на основании противоречивости будущим направлениям развития языка.

PD>С многим можно согласиться. Но вот с этим готов поспорить.
PD>Допустим, VladD2 или кто-то еще оценил непротиворечивость будущему направлению развития и вынес вердикт — не противоречит. Вердикт этот под сомнение ставить не будем, примем как истину. Означает ли это, что можно добавлять ?

[skip]

Ты не учёл во всей этой филиппике одну вещь: переусложнение фичами явно будет мешать развитию языка. Поэтому — все подобные "а давайте тут ещё насадим две пришлёпки сбоку" не пройдёт по этому критерию.

Хотя можно, конечно, это всё потом объявить obsoleted и "кто за две версии не переписал — я не виноват..." (шютка)

PD>ИМХО нет. Язык ведь можно и по принципу языка-оболочки построить. Классический пример — PL/1. Он так и был задуман — взять все хорошее, что есть в Фортране, добавить все хорошее, что есть в Алголе, добавить все хорошее, что есть в Коболе (я не шучу, это именно так и было), получим универсальный язык, который может заменить и Алгол, и Фортран, и Кобол. И это не противоречит будущим направлениям. Понадобилось бы — он и C вместе с C++ в себя бы включил.

PD>На первый взгляд ничего плохого. Сложим в кучу все, что есть, а потом пусть каждый программист выбирает из него то, что ему надо. Но не пошла эта концепция. Размывается при этом структура языка. Из чего-то, что имеет некую внутреннюю логику и построено в соответствии с некоей исходной идеей, язык превращается просто в набор всяких и разных фич, введенных не потому, что без них не обойдешься, а потому, что эта новая фича удобна и красива, хотя на 99% и прежними средствами можно было бы сделать то же самое.

А в результате мы до сих пор не имеем распространённого языка, которому бы можно было сказать "вот этот тип — плавающая точка с порядком +/-100 и точностью 10 десятичных разрядов" и он бы это выполнил.

Вы с водой выплёскиваете ребёнка. Кстати, PL/1 был очень простым — проще C++ или Java.

PD>Вот поэтому я за то, чтобы проверять бритвой все нововведения. Конечно, не надо доводить до абсурда. Но все же, если некое нововвдение таково, что оно на 99% может быть реализовано уже имеющимися средствами, то надо 10 раз подумать, стоит ли его добавлять.


У нас какой контекст? Nemerle или C++?
The God is real, unless declared integer.
Re[19]: уточнение
От: samius Япония http://sams-tricks.blogspot.com
Дата: 15.10.09 10:15
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Вообще-то можно, конечно. Есть такой макрос — offsetof. С его помощью можно получить смещение для любого поля структуры, при любом выравнивании. Так что теоретически задача решается. И даже для функций решается. Но упаси меня бог от такого программирования — это в корне противоречит всем принципам. Я уж не говорю, что в С нет никаких private — protected и придется напрямую копаться в структурах без всякой защиты. Нет уж, только без меня


Инкапсуляцию можно достичь не только за счет модификаторов видимости. Abstraction Barrier, например поможет. Опять таки модификаторы видимости не являются необходимостью. Это только способ сказать "не суй туда пальцы". А барьеры абстракции — более серьезные механизмы.
Re[18]: Inline records
От: Pavel Dvorkin Россия  
Дата: 15.10.09 10:24
Оценка:
Здравствуйте, netch80, Вы писали:

N>Ты не учёл во всей этой филиппике одну вещь: переусложнение фичами явно будет мешать развитию языка.


Не уверен. PL/1 оно не мешало, пока он фактически сам не помер.

>Поэтому — все подобные "а давайте тут ещё насадим две пришлёпки сбоку" не пройдёт по этому критерию.


Ну дай-то бог. Но ИМХО то, что VladD2 предлагает — если это применить к C# (это мое предположение) — окажется именно такой нашлепкой.

N>Хотя можно, конечно, это всё потом объявить obsoleted и "кто за две версии не переписал — я не виноват..." (шютка)


Элементы языка очень редко объявляют obsolete. Я такого почти не помню, разве что object из ТурбоПаскаля, который заменили на class Дельфи, и описание параметров функции в С по K&R. Функции библиотек — другое дело.

Шутку оценил

N>А в результате мы до сих пор не имеем распространённого языка, которому бы можно было сказать "вот этот тип — плавающая точка с порядком +/-100 и точностью 10 десятичных разрядов" и он бы это выполнил.


Между прочим, в PL/1 как раз можно было указать размер мантиссы и порядка. Но не пошло. Видимо, не так уж нужно.

N>Вы с водой выплёскиваете ребёнка. Кстати, PL/1 был очень простым — проще C++ или Java.


Ну в каком-то смысле да.

PD>>Вот поэтому я за то, чтобы проверять бритвой все нововведения. Конечно, не надо доводить до абсурда. Но все же, если некое нововвдение таково, что оно на 99% может быть реализовано уже имеющимися средствами, то надо 10 раз подумать, стоит ли его добавлять.


N>У нас какой контекст? Nemerle или C++?


Я вроде о Немерле ни слова не говорил... А в С++ такое никогда добавлено не будет, уверен.
With best regards
Pavel Dvorkin
Re[30]: доведение ad absurdum
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.10.09 11:59
Оценка: +1 -1
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Еще раз для не понимающих или не желающих понять. Это — демонстрация того, что можно сделать с твоим подходом.
С моим подходом этого сделать нельзя.

PD>Опять демагогия. Свое дело он делает, то есть переворачивает список.

В коде по прежнему нет ничего про переворачивание списка.

PD>Опять ложь. Здесь два конструктора. И будет ли их код устранен или нет — зависит от того, что там есть.

Если не предпринимать специальных усилий, то код лишнего конструктора будет устранён.

PD>Ну и ну! Чего-чего, а уж этого я от тебя не ждал.


PD>Здесь вообще никаких инструкций типа "делай медленно" компилятору нет. Инструкции есть в конструкторах (или других методах), а здесь мы видим лишь вызовы их.

Да, мы видим принудительный вызов промежуточного метода

PD>Доказывается это совершенно элементарно :


PD>Откуда тебе известно, что Array — это массив ? Ты этот вывод из строки

Из твоего описания. Как я уже говорил, этот код — ужасен, потому что не отражает сути происходящего. Тебе процитировать описание метода, которым ты его сопроводил, или самостоятельно найдёшь?

PD>А вдруг этот класс Array вовсе никакого массива и не содержит ? А содержит он только указатель на Linked2List. Конструктор Array по Linked2List просто этот указатель к себе записывает, а конструктор Linked2List по Array переворачивает входной список из Array и берет его к себе! И никакого массива вовсе нет!!! И будет быстро.

PD>Ну и как ?
Отлично. По прежнему разработчик такого кода — кандидат на увольнение. Потому что метод с именем Array не должен заниматься реверсом списка. Ни быстрым, ни медленным.

PD>Да уж какие еще тебе примеры приводить. Хватит и тех, что были.

То есть ты не в состоянии продемонстрировать пример неявного преобразования, который бы иллюстрировал хоть какие-то проблемы.
Ок, ничего другого я и не ожидал.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Inline records
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.10.09 12:50
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Никакое не нарушают. А встречаются не так уж и редко — например функция которая возвращает bool =).
Но ведь не все функции возвращают bool

ЮЖ>Совсем без контрактов получится аналог голой структуры, про значения которой и про взаимосвязи между которыми ничего неизвестно. Вот это мне совсем не нравится. Добавление контрактов на аргументы/состояние/возвращаемое значение в объявление функции, имхо, лучше чем в определение (особенно для постусловий), и еще лучше чем в комментарии.

Ещё раз намекну: наличие контрактов строго ортогонально туплам. То, о чём ты говоришь — это всего лишь предикат первого порядка над результатом функции.
Его наличие или отсутствие никак не связано с тем, что именно возвращается — скаляр, вектор, или структура.

ЮЖ> Безусловно, но я имел ввиду наличие как минимум двух значений, которые возвращаются в качестве результата и взаимоотношениях между ними. Впрочем, к туплам, как таковым это не относится.

Именно.

S>> Статистика быстро подскажет нам, что самые популярные — это NullReferenceException и IndexOutOfRangeException.

ЮЖ>Это не дефекты — это симптомы. Дефект может быть совсем в другом месте.
Естественно. А дефекты, конечно же — обращение к неинициализированному объекту.
В одном случае неинициализирована ссылка, в другом — контейнер не успел получить в себя данные.
Разделение понятий "пустая/непустая ссылка" и "пустой/непустой контейнер" способно задушить эти дефекты на корню. То есть возможность совершить их по-прежнему будет, но ей будет значительно труднее воспользоваться.

ЮЖ>Все рано или поздно упрется в определение эквивалентности двух объектов. Например: берем строку — литерал в коде, состоящий из K символов (известных). шифруем ее каким-нибудь алгоритмом, сжимаем ее, конкатенируем саму с собой, удаляем все буквы 'x', вобщем в том же духе, потом берем два символа с начала строки и два с конца — конкатенируем... а дальше строка 'if (s == "2009")'. Вот здесь компилятор особенно обрадуется...

Возможно. У меня не хватает квалификации, чтобы делать осмысленные предсказания. Но хочу заметить вот что: речь же идет не о попытке выполнить любые вычисления статически. А только о некоторой алгебре. Ну, то есть если мы знаем, что алгоритмы шифрования и компрессии не возвращают пустых строк, то мы можем проследить статус nonEmpty вплоть до момента удаления всех букв x.

ЮЖ>Или тоже самое в присутствии внешних данных и возможных дефектов. Строку можно заменить на какой-нибудь граф объектов...

А зачем?


ЮЖ>Как элиминироваться? От них же (значений) код зависит.


ЮЖ>
ЮЖ>string f(string name, string prefix = "Dear "){
ЮЖ>  return prefix + name;
ЮЖ>}
ЮЖ>//...
ЮЖ>f("Bob!");
ЮЖ>

Элементарно. Выполняем инлайнинг, получаем вместо
public string Test()
{
    return f("Bob!");
}

вот это:
public string Test()
{
    return //f("Bob!");
        "Dear " + "Bob!";
}

а из него —
.ldstr "Dear Bob!";
.return


Но это — не показательный пример. Показательный пример — вот:
public int Multiply(int a, int b = 2)
{
return a * b;
}
[/c#]
Среда исполнения безо всякого инлайнинга может понять, что есть два типа call site — те, кто указывает b, и те, кто не указывает. Для последних будет сгенерирован отдельный код, где выполнена constant propagation, которая в данном случае может привести к более эффективному целевому коду a << 1 (я в курсе, что в наше время это нифига не более эффективный код — это просто иллюстрация).

Такой подход может играть значительно более интересную роль для случаев сложной арифметики — скажем, какие-то операции с матрицами. Метод, который принимает опциональную матрицу домножения, которая по умолчанию равна identity matrix, офигенно выиграет от аналогичной операции — удастся полностью устранить лишнее умножение.
То же самое можно делать
а) вручную, порождая честные отдельные версии методов с передачей значений для пропущенных параметров (и надеясь, что умный компилятор/джит сделает инлайнинг и применит агрессивную оптимизацию)
б) автоматически — на основе profile-guided optimization, которая сама оценит частоты встречаемости разных типов вызовов и породит специализированный код именно для них.


ЮЖ>Можно взять эффекты такого рода:

ЮЖ>
int foo()
ЮЖ>{
ЮЖ>  int x = f();
ЮЖ>  int y = g(); 
ЮЖ>  if(y == 100)
ЮЖ>  {
ЮЖ>    x++;
ЮЖ>  }

ЮЖ>  return (x, y); // y - не будет использоваться.
ЮЖ>}


ЮЖ>Вот такой случай я выше имел ввиду под "зависимыми значениями" результата.

А я имел в виду, что при встраивании кода такого метода в место, где y игнорируется, будет применяться ровно то же самое, что делается сейчас. То есть, если y не используется самой функцией foo() (а в твоём примере это, увы, не так), то останется только вызов g(), результат которого будет проигнорирован ещё до того, как попасть в локальную переменную.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Inline records
От: Юрий Жмеренецкий ICQ 380412032
Дата: 15.10.09 20:21
Оценка:
Здравствуйте, Sinclair, Вы писали:

ЮЖ>>Совсем без контрактов получится аналог голой структуры, про значения которой и про взаимосвязи между которыми ничего неизвестно. Вот это мне совсем не нравится. Добавление контрактов на аргументы/состояние/возвращаемое значение в объявление функции, имхо, лучше чем в определение (особенно для постусловий), и еще лучше чем в комментарии.

S>Ещё раз намекну: наличие контрактов строго ортогонально туплам. То, о чём ты говоришь — это всего лишь предикат первого порядка над результатом функции.
S>Его наличие или отсутствие никак не связано с тем, что именно возвращается — скаляр, вектор, или структура.

Согласен, но здесь другая сторона: можно под контратами понимать то о чем ты говоришь — это "нетипизированные" контракты — просто предикат, который ограничивает множество допустимых значений объекта определенного типа. Но и сама типизация — тоже по сути есть форма контракта. С такой точки зрения принадлежность объекта к некому типу (скаляр, вектор,...) — уже диктует нам перечень допустимых операций над ним. Разница между этими двумя формами в том, что проверку выполнения "статической" составляющей контракта можно переложить на компилятор, а "динамическую" — нет.

Сейчас проверка "динамических" контрактов выполняется в время выполнения и их спецификация обычно выглядит как добавление специальных проверок непосредственно в код, например так:

void f(int x)
{
  Contract.Requires( x > 0 );
  ...
}


с различными вариациями, иногда просто в комментариях (когда проверки дорогие) или в документации. Максимум что можно получить при таком подходе — некоторую проверку статическими анализаторами, генерацию документации, поддержку со стороны IDE и еще что-нибудь подобное. В ограниченных случаях можно заставить компилятор использовать подобные аннотации для оптимизации (__assume в MSVC).

Но программу, в которой может произойти нарушение таких контрактов, все же можно скомпилировать. Да, можно указать максимальный уровень предупреждений и рассматривать их как ошибки, можно использовать различные анализаторы и считать предупреждения от них ошибками. Какой-то процент ошибок такой подход способен отловить.

Но намного 'приятней' получить в таком вызове — 'f(-1)' ошибку компиляции. В таком частном случае — литерал и проверка непосредственно ниже в функции научить компилятор выдавать ошибку — относительно легко (и некоторые подобные случаи компиляторы уже наверняка способны диагностровать), но более сложные случаи отловить так просто не получится. Для их диагностики нужно некоторым образом 'типизировать' такие контраты. + я исхожу из того, что набор объектов скорее всего будет иметь какие-либо органичения на множество допустимых значений (а тупл — это как раз такой объект).


S>>> Статистика быстро подскажет нам, что самые популярные — это NullReferenceException и IndexOutOfRangeException.

ЮЖ>>Это не дефекты — это симптомы. Дефект может быть совсем в другом месте.
S>Естественно. А дефекты, конечно же — обращение к неинициализированному объекту.

Не совсем. Впрочем это зависит от терминологии. Я придерживаюсь такой:

Возьмем разыменование нулевого указателя в С++ — там это не приводит к исключениям с т.з. языка и пусть программа сразу при этом падает. Падение — это отказ (failure), т.е. неспособность дальнейшей работы. Причина по которой появился нулевой указатель — это дефект. Отказ в такой функции:

// p никогда не равен нулю
void f(int* p)
{
 *p = 0;
}


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

В свою очередь постороение систем, толерантных к дефектам всегда ведет к усложнению (привет недавним обсуждениям) и появлению избыточности. Это может быть дублирование, или использование памяти с ECC и т.д. В этом случае невозможно устранить причину. В случае с программированием это не совсем так — гарантировать отсутствие ошибок типизации компиляторы уже научились, и это привело к тому что, например, тестировать то, что гарантирует компилятор уже не нужно (избавились от избыточности). [Я не говорю что абсолютно все можно переложить на компилятор]

S>Разделение понятий "пустая/непустая ссылка" и "пустой/непустой контейнер" способно задушить эти дефекты на корню. То есть возможность совершить их по-прежнему будет, но ей будет значительно труднее воспользоваться.

Согласен.

ЮЖ>>Все рано или поздно упрется в определение эквивалентности двух объектов. Например: берем строку — литерал в коде, состоящий из K символов (известных). шифруем ее каким-нибудь алгоритмом, сжимаем ее, конкатенируем саму с собой, удаляем все буквы 'x', вобщем в том же духе, потом берем два символа с начала строки и два с конца — конкатенируем... а дальше строка 'if (s == "2009")'. Вот здесь компилятор особенно обрадуется...

S>Возможно. У меня не хватает квалификации, чтобы делать осмысленные предсказания. Но хочу заметить вот что: речь же идет не о попытке выполнить любые вычисления статически. А только о некоторой алгебре. Ну, то есть если мы знаем, что алгоритмы шифрования и компрессии не возвращают пустых строк, то мы можем проследить статус nonEmpty вплоть до момента удаления всех букв x.

Насколько я поинимаю, картина выглядит примерно так:

Вот такое ограничение:
void f(int v : v > 0)


для компилятора должно выглядеть так (грубо):

struct int_>0 // это такой 'идентификатор'
{
  int_>0(int a) : {v = a;}
  int v;
}

void f(int_>0 v);


Т.е. фактически ограничение — это часть типа. Проблема в том, что бы компилятор смог понять что:

int_>0   a = 0; // ошибка, литерал имеет тип int_0
int_0    b = 0; // ок
int_>-5  c = b; // ok
         b = c; // ошибка


Т.е. здесь нужно обеспечить приводимость типов исходя из контекста. В случаях если предикаты специфицируют диапазоны значений, вывод типов даже при простых арифметических операциях усложняется. Еще хуже дело обстоит там где одно и тоже значение можно получить в результате разных действий (а это происходет очень часто) — типы должны быть эквивалентными:

"1" + "1" + "1" — имеет такой же тип как и резльтат операции 'из строки "111abc" вырезать последние три символа'. Для встроенных чисел и строк компилятор может использовать их свойства, для пользовательских типов — такие свойства нужно задавать явно (observable behaviour), либо выводить их конструктивно, из реализации методов (что в конечном итоге сведется к использованию свойств встроенных примитивов). Это по сути и есть пред/постусловия и инварианты.

С мутабельными (хотя это непринципиально) объектами и циклами веселее:
container c;
int_>100 n = ...

for(int i = 0; i<=n; i++)
 container.Add(n + i);

это эквивалентно такому:
container.Add(n + 0)
container.Add(n + 1)
...
container.Add(n + n)


Какой тип после этого имеет контейнер? Фактически здесь вся алгебра опирается на пред/постусловия: они разобъют множество допустимых операций на несколько классов с определенными переходами между ними (т.е. операция Add для пустого контейнера переводит его в состояние 'не пустой', и т.д.). Можно ограничится этим, но этого как-то мало... Вот вывод типа произвольного объекта, находящегося в нем — уже намного лучше. В этом случае тип
возвращаемого значения операцией At будет представлять по сути последовательность действий, 'сжатых' в том месте, где были циклы с сохранением дополнительных параметров для вычисления ограничений на значения, т.е. 'каждое значение с индексами от 0 до n — 1 имеет значение от n до 2n, где n > 100. В общем случае, для произвольного объекта, для каждого геттера (точнее функции, которая возвращает значение, зависимое от состояния) придется перевычислять такой тип после каждой операции, способной повлиять на содержимое.

ЮЖ>>Или тоже самое в присутствии внешних данных и возможных дефектов. Строку можно заменить на какой-нибудь граф объектов...

S>А зачем?
Для иллюстрации возникающих проблем. Просто что-то, что сложнее строки.

ЮЖ>>Как элиминироваться? От них же (значений) код зависит.

S>Элементарно. Выполняем инлайнинг, получаем вместо
...
S>Но это — не показательный пример. Показательный пример — вот:
S>public int Multiply(int a, int b = 2)
S>{
S> return a * b;
S>}
S>[/c#]
S>Среда исполнения безо всякого инлайнинга может понять, что есть два типа call site — те, кто указывает b, и те, кто не указывает. Для последних будет сгенерирован отдельный код, где выполнена constant propagation, которая в данном случае может привести к более эффективному целевому коду a << 1 (я в курсе, что в наше время это нифига не более эффективный код — это просто иллюстрация).

Это все верно, только вот это — 'a << 1' напрямую зависит от значения b =) Но, действительно при сравнении с набором возвращаемых значений — это не хромота, просто оптимизация выполняется 'задом наперед' и упирается либо в побочные эффекты, либо в зависимость от других данных (не в смысле ее невозможности).
Re[6]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 15.10.09 20:34
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>Нет, по крайне мере, в стандартной библиотеке record'а нет. В коде возвращался созданный экземпляр класса inlinerecord.


Я тебя (да и себя тоже) нагло обманул, он таки-есть — кортеж с именованными полями, неизменяемый, но увы, не поддерживающий структурную идентичность. Оказывается в питоне 2.6, в модуле collections появилась фабричная функция collections.namedtuple(typename, field_names[, verbose]) — она конструирует в глобальном пространстве имен новый тип с именем typename, наследующим тип tuple и предоставляющим возможность доступа к элементам tuple по их именам field_names. Т.е. мой пример с ее использованием был бы таким:

inlinerecords.py
from collections import namedtuple

def returnsnames(names):
    def wrapper(f):
        return lambda *args, **kwds: namedtuple('NamedTuple', names)(*f(*args, **kwds))
    return wrapper


Пробуем:

from inlinerecords import returnsnames

@returnsnames('topicid title author')
def getmessage2():
    return (3564395, 'Inline records', 'VladD2')

@returnsnames('a b c')
def getmessage3():
    return (3564395, 'Inline records', 'VladD2')

a = getmessage1()
b = getmessage2()
c = getmessage3()
print a.a, a.b, a.c
print b.a, b.b, b.c
print c.a, c.b, c.c

try:
    a.author = 'kochetkov.vladimir'
except TypeError:
    print 'immutability test passed'
else:
    print 'immutability test failed'

if a != c and a==b:
    print 'identity test passed'
else:
    print 'identity test failed'


получаем:

3564395 Inline records VladD2
3564395 Inline records VladD2
3564395 Inline records VladD2
immutability test passed
identity test failed


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

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[16]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.10.09 20:59
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Замечательная мысль. Поитом еще одного наследника сделаем


PD>struct EinsteinPoint {

PD> struct POINT3D point3D;
PD> int time;
PD>};

PD>и в инструкции напишем, что поля называются point3D.plain.x, point3D.plain.y, point3D.z и почему-то просто time;



Все еще проще. Для доступа к полям напишем функции. Сами типы опишем в отдельном файле и публичного описания не дадим.
Таким образом получим инкапсуляцию.
Ну, а наследование и полиморфизм сделаем как нам нравится. Один из способов тебе уже описали.

А ты думал HANDLE В Win API — это что?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[20]: уточнение
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.10.09 21:01
Оценка:
Здравствуйте, samius, Вы писали:

S>Инкапсуляцию можно достичь не только за счет модификаторов видимости. Abstraction Barrier, например поможет. Опять таки модификаторы видимости не являются необходимостью. Это только способ сказать "не суй туда пальцы". А барьеры абстракции — более серьезные механизмы.


Более того, в плюсах как раз можно легко плюнуть на запреты путем манипуляции с указателями, а то и просто написав левый хэадер.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.10.09 21:14
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

VD>>Ну, и поддерживается ли для типа record (если он существует) структурная идентичность? Ну, когда две независимо созданные записи идентичны при условии, что идентичны значения и имена полей.


KV>Во втором варианте кода — да, поддерживается:


Здорово!

Но все же здесь получается, что возвращаются экземпляры класса. Идентичность для них достигается путем переопределения метода сравнения. Не получится ли так, что в некотором контексте объекты все же окажутся не равны?

ЗЫ

А вообще, скрипты основанные на модели прототипного ООП и мета-классов весьма гибки в области метапрограммирования. Но платой за это является производительность. Ведь такие вот выкрутасы уже никак не оптимизируешь. Тут само представление объекта должно быть хэш-таблицей. Иначе фокус не пройдет.

Я же рассуждал о статически типизированных языках в которых все определяется декларацией.
Собственно создать такой перепаковщик можно и на базе макросов того же немерле. Проблема в том, что это не очень красивое решение, так как операция сравнения будет весьма не шустрой (придется сравнивать имена и значения полей в рантайме), и не всегда корректной, так как порой важен фактический тип, а не наличие у него операции сравнения. Скажем при передаче в качестве параметра.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[14]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.10.09 21:23
Оценка: +1
Здравствуйте, netch80, Вы писали:

N>Если формально подходить к вопросу — это более чем возможно. Очень многие вещи в WinAPI реализуются в userland'е использующего процесса — например, к этому относится значительная часть GUI, и залезть в область данных рисуемого приложением окна (при том, что адрес области добывается через GetWindowLong) — столь же легко, как запороть любые другие данные.


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

Вопрос в другом. Структура типов в Вин АПИ не описана. Ты получаешь хэндл и функции оперирующие с ним. Для пущей инкапсуляции некоторые хэндлы даже шифруются (если не ошибаюсь, на них накладывается хэш или они проверяются по таблице). Так что инкапсуляция получается куда более надежной чем в С++.

N>Вообще, ваш спор существенно ни о чём.


Это ваши замечания не о чем. А я товарищу просто пытался продемонстрировать, что эмулировать можно все что угодно. И то что что-то можно с горем пополам сэмулировать, еще не значит, что это нужно делать.

Для него же кортежи не нужны, так как их можно чем-то заменить или сэмулировать. А вот классы — это ОБЯЗАТЕЛЬНО нужно, так как он без них жить не может. Короче костность мышления и неумение абстрагироваться от своих привычек.

VD>>Если ты еще не понял, то разговоры на тему зачем нужны кортежи ничем не отличаются от разговоров на тему зачем нужны классы. Просто ты предвзято относишься к проблеме.


N>Вот тут +100. Наличие механизма, упрощающего написание и понимание типичных конструкций, всегда полезно.


Ну, так а какой смысл докапываться к деталям? Ты меня прости, но есть у тебя такой грешок. Привязаться к несущесвтенным деталям или термину и уйти в сторону.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Inline records
От: vdimas Россия  
Дата: 15.10.09 21:27
Оценка:
Здравствуйте, lomeo, Вы писали:

L>[/code]


L>Я вообще не вижу, когда второе может быть выгоднее. light records всё таки используются немного для других целей.


Для каких других?
По твоему имена аргументов методов хранятся в метаинформации того же дотнет просто так? Ведь можно было бы обойтись хранением в метаинформации только типов (как в obj-файлах C++), а сигнатуру давать в виде хелпа.
Там где 2-3 поля тупла, вероятность ошибки не высока... но ведь очевидно, что чем больше полей, тем больше требуется удобств.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[31]: доведение ad absurdum
От: Pavel Dvorkin Россия  
Дата: 16.10.09 01:50
Оценка:
Здравствуйте, Sinclair, Вы писали:

PD>>Ну и ну! Чего-чего, а уж этого я от тебя не ждал.


PD>>Здесь вообще никаких инструкций типа "делай медленно" компилятору нет. Инструкции есть в конструкторах (или других методах), а здесь мы видим лишь вызовы их.

S>Да, мы видим принудительный вызов промежуточного метода

Да, мы его видим. Только из этого не следует. что он так-таки уж "медленный". Это следует или не следует только из его кода, а не вызова.

PD>>Доказывается это совершенно элементарно :


PD>>Откуда тебе известно, что Array — это массив ? Ты этот вывод из строки

S>Из твоего описания.

Из моих слов, да. Но и только. А компилятор мои слова не слышал. И поэтому никаких указаний компилятору "делай медленно" здесь нет. И ты это не можешь не понимать.

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


Не надо. Если тебе надо в сотый раз объяснять чем отличается демонстрационный пример, единственное назначение котороо — продемонстрировать суть проблемы, от кода, который может быть включен в рабочий, то , видимо, дело вполне безнадежно.

PD>>А вдруг этот класс Array вовсе никакого массива и не содержит ? А содержит он только указатель на Linked2List. Конструктор Array по Linked2List просто этот указатель к себе записывает, а конструктор Linked2List по Array переворачивает входной список из Array и берет его к себе! И никакого массива вовсе нет!!! И будет быстро.

PD>>Ну и как ?
S>Отлично. По прежнему разработчик такого кода — кандидат на увольнение. Потому что метод с именем Array не должен заниматься реверсом списка. Ни быстрым, ни медленным.

Ну и аргументация. Делать какие бы то ни было выводы на основании имени, да еще в демонстрационном примере — это уж полное признание отсутствия аргументов.

См. выше про демонстрационный пример.

PD>>Да уж какие еще тебе примеры приводить. Хватит и тех, что были.

S>То есть ты не в состоянии продемонстрировать пример неявного преобразования, который бы иллюстрировал хоть какие-то проблемы.
S>Ок, ничего другого я и не ожидал.

Я тоже. Вместо аргументов — сплошная демагогоия.
With best regards
Pavel Dvorkin
Re[17]: Inline records
От: Pavel Dvorkin Россия  
Дата: 16.10.09 02:03
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>Все еще проще. Для доступа к полям напишем функции. Сами типы опишем в отдельном файле и публичного описания не дадим.


Это все, конечно, замечательно, а как экземпляры создавать будем ? Функцию, которая их создает в куче, описать несложно, а что она вернет-то ? void* ? Типов-то нет. Что-то примерно так

void* p = MakePoints(10);
// POINT* p = new POINT[10];

Красота!

А автоматические переменные как создавать будем ? Там и функция не поможет.
With best regards
Pavel Dvorkin
Re[18]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 16.10.09 03:32
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

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


VD>>Все еще проще. Для доступа к полям напишем функции. Сами типы опишем в отдельном файле и публичного описания не дадим.


PD>Это все, конечно, замечательно, а как экземпляры создавать будем ? Функцию, которая их создает в куче, описать несложно, а что она вернет-то ? void* ? Типов-то нет. Что-то примерно так


PD>void* p = MakePoints(10);

PD>// POINT* p = new POINT[10];

PD>Красота!

Скорее
POINTS points = MakePoints(10);
POINT p = GetPoint(points, 10);

При такой инкапсуляции мы можем потерять размер записи. Здесть POINTS и POINT — это лишь хэндлы.

PD>А автоматические переменные как создавать будем ? Там и функция не поможет.

Хм.
Надо было полиморфизм и инкапсуляцию — вот они. А с автоматическими переменными они не совместимы (даже в C++). Да и не по оккамовски это, когда можно явно двумя строчками создать и освободить
Re[19]: Inline records
От: Pavel Dvorkin Россия  
Дата: 16.10.09 04:50
Оценка:
Здравствуйте, samius, Вы писали:

S>Скорее

S>
S>POINTS points = MakePoints(10);
S>POINT p = GetPoint(points, 10);
S>

S>При такой инкапсуляции мы можем потерять размер записи. Здесть POINTS и POINT — это лишь хэндлы.

Если POINT и POINTS — это только хендлы — пожалуйста, дай мне их описание. typedef или #define. И ты сразу поймешь, что кроме void* у тебя ничего нет — типов-то нет. И никто мне не помешает написать

points = p;

и даже предупреждения не будет, а потом все с ума сойдут.


S>Надо было полиморфизм и инкапсуляцию — вот они. А с автоматическими переменными они не совместимы (даже в C++)


Что-что ? Инкапсуляция и наследование не совместимы с автоматическими переменными ?

class Base
{ public:
virtual void f() {}
};
class Derived : public Base
{public:
 void f(){}
};
int main()
{
Derived d;
Base *pb = &d;
pb->f();
}



Тут тебе не C#


>Да и не по оккамовски это, когда можно явно двумя строчками создать и освободить


Не пойдет. Создание в куче и создание на стеке — это немного разные вещи . А уничтожить стек во имя полной оккамовости я все же не готов
With best regards
Pavel Dvorkin
Re[20]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.10.09 05:43
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:


S>>Надо было полиморфизм и инкапсуляцию — вот они. А с автоматическими переменными они не совместимы (даже в C++)


PD>Что-что ? Инкапсуляция и наследование не совместимы с автоматическими переменными ?


PD>
PD>class Base
PD>{ public:
PD>virtual void f() {}
PD>};
PD>class Derived : public Base
PD>{public:
PD> void f(){}
PD>};
PD>int main()
PD>{
PD>Derived d;
PD>Base *pb = &d;
pb->>f();
PD>}

PD>



PD>Тут тебе не C#


За такой код руки по шею отрубать надо.
Попробуй pb передать куда-либо.

Вообще то что сслыка на объект может жить дольше самого объекта — охренительный баг в дизайне языке.
Re[21]: Inline records
От: Pavel Dvorkin Россия  
Дата: 16.10.09 06:24
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>За такой код руки по шею отрубать надо.

G>Попробуй pb передать куда-либо.

А за такой тоже ?


char szText[100] = "пример";
char *p = strchr(szText, "п");


Тут ведь тоже самое — вдруг эту p кто-нибудь передаст куда-то. Кошмар!

Кстати, и передать вполне можно, пока указатель действителен.


char szText[100] = "пример";
char * p = strchr(szText, "п");
printf("%s\n", p);


Так что ты всем авторам всех учебников головы пооотрываешь.

G>Вообще то что сслыка на объект может жить дольше самого объекта — охренительный баг в дизайне языке.


No comments, поскольку ты не понимаешь разницы между ссылкой на объект и указателем на область памяти.
With best regards
Pavel Dvorkin
Re[22]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.10.09 06:34
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


G>>За такой код руки по шею отрубать надо.

G>>Попробуй pb передать куда-либо.

PD>А за такой тоже ?



PD>
PD>char szText[100] = "пример";
PD>char *p = strchr(szText, "п");
PD>


PD>Тут ведь тоже самое — вдруг эту p кто-нибудь передаст куда-то. Кошмар!

Действительно кошмар. Плачевно что ты этогоне понимаешь.

PD>Кстати, и передать вполне можно, пока указатель действителен.

Ну коненчо. Только как принимающая сторона узнает о действительности указателя?



PD>
PD>char szText[100] = "пример";
PD>char * p = strchr(szText, "п");
PD>printf("%s\n", p);
PD>


PD>Так что ты всем авторам всех учебников головы пооотрываешь.

За такой код — да.

char * szText = "пример";
char * p = strchr(szText, "п");
printf("%s\n", p);


А вот так вполне ОК, ибо литералы не разрушатся при выходе их скоупа и ссылки на них будут действительны.

G>>Вообще то что сслыка на объект может жить дольше самого объекта — охренительный баг в дизайне языке.

PD>No comments, поскольку ты не понимаешь разницы между ссылкой на объект и указателем на область памяти.

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

Да и получение указателя на стек — не самая безопасная операция назвисимо от того зачем оно нужно.
Re[15]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 16.10.09 07:09
Оценка:
Здравствуйте, VladD2, Вы писали:

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

N>>Если формально подходить к вопросу — это более чем возможно. Очень многие вещи в WinAPI реализуются в userland'е использующего процесса — например, к этому относится значительная часть GUI, и залезть в область данных рисуемого приложением окна (при том, что адрес области добывается через GetWindowLong) — столь же легко, как запороть любые другие данные.
VD>Если мы говорим о том, что можно путем неинтерпретации памяти и недужей смекалки залезть в кишки, то конечно — да, инкапсуляцию можно нарушить. Но это опять докапывание до не существенных деталей. В С++ тоже без проблем можно обойти защиту если залезть в помять с помощью указателей.

Собственно это одно из того, что я хотел сказать: что ни C ни C++ в своих средствах делается защиту от наиболее грубого подхода (который тем не менее явно виден) и не делается от явных диверсий (которые достаточно легко замаскировать). С этой точки зрения я не вижу между ними существенной разницы. И пример с WinAPI, несмотря на то, что идёт в нужную сторону, неполон и неточен.

VD>Вопрос в другом. Структура типов в Вин АПИ не описана. Ты получаешь хэндл и функции оперирующие с ним. Для пущей инкапсуляции некоторые хэндлы даже шифруются (если не ошибаюсь, на них накладывается хэш или они проверяются по таблице). Так что инкапсуляция получается куда более надежной чем в С++.


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

N>>Вот тут +100. Наличие механизма, упрощающего написание и понимание типичных конструкций, всегда полезно.

VD>Ну, так а какой смысл докапываться к деталям? Ты меня прости, но есть у тебя такой грешок. Привязаться к несущесвтенным деталям или термину и уйти в сторону.

А вот давай не смешивать мух с котлетами. Только в этом треде было две совершенно разных ситуации: с "нетипизированным языком" и с WinAPI. Сначала про вторую. Если ты внимательно посмотришь, то увидишь, что я явно сказал и подчеркнул, что это некоторый уход в сторону, и заметил, что в целом я с тобой больше согласен, чем с PD. А с нетипизированностью — мне было интересно, может, у тебя какие-то совершенно новые идеи, которых я ещё не слышал.
В общем случае ты, насколько я вижу, неготов к тому, что если ты или употребляешь термины не по назначению, приводишь недостаточно адекватные примеры или ещё в чём-то ошибаешься — то обязательно кто-то поправит или возразит. Это совершенно неправильная позиция. Причём я ещё спокойно к этому отношусь, а записные флеймеры могут из этого раздуть ещё сотню сообщений. Но ты почему-то ругаешь меня, а не их.:))
The God is real, unless declared integer.
Re[23]: Inline records
От: Pavel Dvorkin Россия  
Дата: 16.10.09 07:15
Оценка: -2
Здравствуйте, gandjustas, Вы писали:


PD>>Тут ведь тоже самое — вдруг эту p кто-нибудь передаст куда-то. Кошмар!

G>Действительно кошмар. Плачевно что ты этогоне понимаешь.

Да не только я. Весь ISO не понимает. Нет под рукой документа, из которого можно скопировать, , а PDF защищенный , так что приведу картинку

http://files.rsdn.ru/187/basederived.png

Молодец! Всем сразу из ISO будем головы резать или по очереди ?


PD>>Кстати, и передать вполне можно, пока указатель действителен.

G>Ну коненчо. Только как принимающая сторона узнает о действительности указателя?

Язык учи и не задавай глупые вопросы. Это тысячу раз обсуждалось.

G>А вот так вполне ОК, ибо литералы не разрушатся при выходе их скоупа и ссылки на них будут действительны.


Ну и каша у тебя в голове!

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


И все же помедитируй насчет того, когда указатель показывает на объект и когда на область памяти. Хинт — подумай, что такое void*, и на какой "объект" он показывает

G>Да и получение указателя на стек — не самая безопасная операция назвисимо от того зачем оно нужно.


Слушай, лучше не надо комментариев насчет С++. Ты в нем разбираешься не лучше, чем я в Немерле
With best regards
Pavel Dvorkin
Re[24]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.10.09 07:48
Оценка: +1 -1
Здравствуйте, Pavel Dvorkin, Вы писали:

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



PD>>>Тут ведь тоже самое — вдруг эту p кто-нибудь передаст куда-то. Кошмар!

G>>Действительно кошмар. Плачевно что ты этогоне понимаешь.

PD>Да не только я. Весь ISO не понимает. Нет под рукой документа, из которого можно скопировать, , а PDF защищенный , так что приведу картинку

PD>http://files.rsdn.ru/187/basederived.png
PD>Молодец! Всем сразу из ISO будем головы резать или по очереди ?
не тупи, иллюстрация наследования никак не является кодом реального приложения.
И то что так можно делать, не означает что так надо делать.


PD>>>Кстати, и передать вполне можно, пока указатель действителен.

G>>Ну коненчо. Только как принимающая сторона узнает о действительности указателя?
PD>Язык учи и не задавай глупые вопросы. Это тысячу раз обсуждалось.
Да я знаю язык, ты ответь на этот простой вопрос.

G>>А вот так вполне ОК, ибо литералы не разрушатся при выходе их скоупа и ссылки на них будут действительны.

PD>Ну и каша у тебя в голове!
Я где-то неправ?

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

PD>И все же помедитируй насчет того, когда указатель показывает на объект и когда на область памяти. Хинт — подумай, что такое void*, и на какой "объект" он показывает
Не съезжай с темы. Изначально был указатель на объект, который надо бы передать куда-то.
Что ты там будешь делать с viod* — твои личные половые трудности.

G>>Да и получение указателя на стек — не самая безопасная операция назвисимо от того зачем оно нужно.

PD>Слушай, лучше не надо комментариев насчет С++. Ты в нем разбираешься не лучше, чем я в Немерле
Даже ответить ничго не можешь
Re[9]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 16.10.09 08:39
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Для каких других?


Когда полей мало выгоднее пользоваться таплами, когда в них уже можно запутаться — мы их начинаем именовать. Например, сравни определение небольшого АТД в Haskell и того, в котором полей больше трёх. Его поля начинают именовать, под это дело есть синтаксический сахар как для выражений, так и для паттернов. Это полная аналогия разницы лёгких записей с именованными полями и таплами.

Выгода использования таплов (при малом количестве полей) проявляется в том, что паттерн получается очень компактным, создание значения тоже, плюс (но это и у записей) — нам не нужно создавать дополнительный тип.

Очевидно, что в критерии "полей достаточно мало для тапла" есть немного субъективного, т.к. понимание кода вещь тоже субъективная. По моему это и есть место для спора

V>По твоему имена аргументов методов хранятся в метаинформации того же дотнет просто так? Ведь можно было бы обойтись хранением в метаинформации только типов (как в obj-файлах C++), а сигнатуру давать в виде хелпа.


Я не знаю дотнет. Ты про IDE говоришь в выделенном?

V>Там где 2-3 поля тупла, вероятность ошибки не высока... но ведь очевидно, что чем больше полей, тем больше требуется удобств.


Разумеется, не стоит пользоваться таплами с надцатью полями.
Re[25]: Inline records
От: Pavel Dvorkin Россия  
Дата: 16.10.09 08:41
Оценка: -2 :)
Здравствуйте, gandjustas, Вы писали:

G>не тупи, иллюстрация наследования никак не является кодом реального приложения.

G>И то что так можно делать, не означает что так надо делать.
PD>>>>Кстати, и передать вполне можно, пока указатель действителен.
G>>>Ну коненчо. Только как принимающая сторона узнает о действительности указателя?
PD>>Язык учи и не задавай глупые вопросы. Это тысячу раз обсуждалось.
G>Да я знаю язык, ты ответь на этот простой вопрос.

В форум по С++! Там тебе на все такие вопросы ответят. Там много желающих отвечать новичкам на их вопросы. А меня такое последний раз интересовало лет 15 назад, когда я С изучал.

G>Даже ответить ничго не можешь


И не буду. Отвечать на все глупые вопросы я не намерен. Разберись сначала с основами С++, понятием времени жизни, а потом приходи.
With best regards
Pavel Dvorkin
Re[26]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.10.09 08:51
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

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


G>>не тупи, иллюстрация наследования никак не является кодом реального приложения.

G>>И то что так можно делать, не означает что так надо делать.
PD>>>>>Кстати, и передать вполне можно, пока указатель действителен.
G>>>>Ну коненчо. Только как принимающая сторона узнает о действительности указателя?
PD>>>Язык учи и не задавай глупые вопросы. Это тысячу раз обсуждалось.
G>>Да я знаю язык, ты ответь на этот простой вопрос.

PD>В форум по С++! Там тебе на все такие вопросы ответят. Там много желающих отвечать новичкам на их вопросы. А меня такое последний раз интересовало лет 15 назад, когда я С изучал.


G>>Даже ответить ничго не можешь


PD>И не буду. Отвечать на все глупые вопросы я не намерен. Разберись сначала с основами С++, понятием времени жизни, а потом приходи.


Слив засчитан.
Re[24]: ссылка на стандарт
От: Pavel Dvorkin Россия  
Дата: 16.10.09 09:34
Оценка:
привожу на всякий случай

http://www.kuzbass.ru:8086/docs/isocpp/derived.html
With best regards
Pavel Dvorkin
Re[26]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 17.10.09 20:54
Оценка: :))) :))
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Разберись сначала с основами С++, понятием времени жизни, а потом приходи.


Между прочим, я бросил "разбираться с основами С++" и перешел, ээээ... на другие языки, именно тогда, когда ко мне пришло понимание о времени жизни. Ибо я понял, что она слишком коротка, чтобы тратить ее на сколь-нибудь глубокое изучение С++

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[27]: Inline records
От: Pavel Dvorkin Россия  
Дата: 18.10.09 04:39
Оценка: :)
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>Между прочим, я бросил "разбираться с основами С++" и перешел, ээээ... на другие языки, именно тогда, когда ко мне пришло понимание о времени жизни. Ибо я понял, что она слишком коротка, чтобы тратить ее на сколь-нибудь глубокое изучение С++


Видишь ли, я разобрался с этими понятиями еще когда С++ у нас не было, а был просто Turbo C. А переходить было тоже некуда, разве что обратно на ТурбоПаскаль, но там то же самое, даже сложнее немного (вложенные функции). Так что мне не пришлось потом с чем-то еще разбираться. С тех пор так и живу

Вот, кстати, пример того, как указатель на локальную переменную передается, страшно вымолвить для gandjustas , в другой поток. И ничего, все будет работать. Но КодТ совершенно прав — "так делать категорически можно, но умеючи" . А для этого нужно все же язык знать, и главное, понимать, что делаешь. Если понимаешь, то никаких проблем не возникнет, так как правила-то простенькие. А вот если не понимать, то да, получится одна ерунда.

http://rsdn.ru/forum/cpp.applied/3549503.1.aspx
Автор: Кодт
Дата: 27.09.09


Я бы так сказал — язык С++ дает мне свободу делать все, что я хочу. А это неизбежно сопряжено с возможностью сделать и ошибку. Чтобы свободой как следует воспользоваться, надо хорошо понимать, что при этом можно, и что нельзя делать.
With best regards
Pavel Dvorkin
Re[27]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.10.09 06:20
Оценка: +1
Здравствуйте, kochetkov.vladimir, Вы писали:

PD>>Разберись сначала с основами С++, понятием времени жизни, а потом приходи.


KV>Между прочим, я бросил "разбираться с основами С++" и перешел, ээээ... на другие языки, именно тогда, когда ко мне пришло понимание о времени жизни. Ибо я понял, что она слишком коротка, чтобы тратить ее на сколь-нибудь глубокое изучение С++ ;)


Хм.... +10. Или даже +100.

Причём я видел его судьбу и пользовался ещё начиная с первой версии языка. И видел, что всё текущее развитие глубоко логично в практически всех своих деталях: задачу "превратить Си в объектно-ориентированный язык не сломав совместимость с Си" крайне сложно было бы решить иначе, чем сейчас. (Это вам не PHP, где развитие проводится методом лотереи из готовых расширений не приведя их в хоть какую-то консистентность). Но тем более видно, что с момента введения шаблонов язык принципиально поменял характер, и теперь это фактически совсем другой язык. И что неприятно — сложность его складывается из сложности всех уровней.
The God is real, unless declared integer.
Re[28]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.10.09 06:33
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Вот, кстати, пример того, как указатель на локальную переменную передается, страшно вымолвить для gandjustas :-) , в другой поток. И ничего, все будет работать. Но КодТ совершенно прав — "так делать категорически можно, но умеючи" :-). А для этого нужно все же язык знать, и главное, понимать, что делаешь. Если понимаешь, то никаких проблем не возникнет, так как правила-то простенькие. А вот если не понимать, то да, получится одна ерунда.


PD>http://rsdn.ru/forum/cpp.applied/3549503.1.aspx
Автор: Кодт
Дата: 27.09.09


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

PD>Я бы так сказал — язык С++ дает мне свободу делать все, что я хочу. А это неизбежно сопряжено с возможностью сделать и ошибку. Чтобы свободой как следует воспользоваться, надо хорошо понимать, что при этом можно, и что нельзя делать.


Осталось понять, какой смысл так делать. Безусловно, ~1% населения согласится ради адреналина жить в доме, где внешней стены просто нет (вопрос климата опускаем). Но остальные захотят таки вернуть на место стену, и это не вопрос "трусости" или "нежелания свободы" — это вопрос экономии усилий на ненужном.
The God is real, unless declared integer.
Re[29]: Inline records
От: Pavel Dvorkin Россия  
Дата: 18.10.09 07:57
Оценка:
Здравствуйте, netch80, Вы писали:

N>Ну хорошо, обложишь ты такой кусок кода страницей комментариев, что оно такое и зачем, и в каком случае можно так писать, и как можно менять. А завтра окажется, что надо запускать на платформе, на которой все локальные стеки разных тредов мапятся на один и тот же адрес. Язык этого не запрещает — ему пофиг треды (по крайней мере до наступления коммуни^W нового стандарта).


В стандарте языка тредов нет, верно. В библиотеке RTL C++ для Windows треды присутствуют, так как __beginthreadex — функция из RTL. Что такое
стеки, маппирующиеся на один и тот же адрес — не знаю, но с потоками Windows это не совместимо — при запуске потока надо указать размер его стека, и стек будет, конечно, новый . Так что если придется это переписывать под иную платформу — здесь все равно придется изменять все, а не только этот кусочек.

PD>>Я бы так сказал — язык С++ дает мне свободу делать все, что я хочу. А это неизбежно сопряжено с возможностью сделать и ошибку. Чтобы свободой как следует воспользоваться, надо хорошо понимать, что при этом можно, и что нельзя делать.


N>Осталось понять, какой смысл так делать. Безусловно, ~1% населения согласится ради адреналина жить в доме, где внешней стены просто нет (вопрос климата опускаем). Но остальные захотят таки вернуть на место стену, и это не вопрос "трусости" или "нежелания свободы" — это вопрос экономии усилий на ненужном.


Все очень просто. Дело в том, что стены не нет вообще, а она убираемая. И если 99% хотят все же стену — пусть вернут ее на место. А вот Мак Сим хотел иное (выделено мной)

Ладно, ладно, спать. И когда же мне удастся поспать
по-человечески? В большой просторной комнате, на свежих
простынях... Что у них здесь за обычай -- спать по многу раз на
одной простыне?.. Да, на свежих простынях, а перед сном
почитать хорошую книгу, потом убрать стену в сад, выключить
свет и заснуть...

(С) Стругацкие, Обитаемый остров

http://thelib.ru/books/strugackie_arkadiy_i_boris/obitaemiy_ostrov-read.html

With best regards
Pavel Dvorkin
Re[28]: Inline records
От: Pavel Dvorkin Россия  
Дата: 18.10.09 08:01
Оценка:
Здравствуйте, netch80, Вы писали:

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


В значительной мере верно.

>И что неприятно — сложность его складывается из сложности всех уровней.


Тоже верно.

Я вовсе не утверждаю, что С++ идеален. Более того, мне многое в нем не нравится. Но альтернативы-то пока нет. Скажу сразу — все управляемые языки в качестве альтернативы не предлагать, это не обсуждается.
With best regards
Pavel Dvorkin
Re[29]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.10.09 12:59
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Я вовсе не утверждаю, что С++ идеален. Более того, мне многое в нем не нравится. Но альтернативы-то пока нет.


Ada?

PD> Скажу сразу — все управляемые языки в качестве альтернативы не предлагать, это не обсуждается.


"Сразу скажу" — не понимаю причин такого ограничения.
The God is real, unless declared integer.
Re[28]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.10.09 17:12
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Вот, кстати, пример того, как указатель на локальную переменную передается, страшно вымолвить для gandjustas , в другой поток. И ничего, все будет работать. Но КодТ совершенно прав — "так делать категорически можно, но умеючи" . А для этого нужно все же язык знать, и главное, понимать, что делаешь. Если понимаешь, то никаких проблем не возникнет, так как правила-то простенькие. А вот если не понимать, то да, получится одна ерунда.

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

PD>http://rsdn.ru/forum/cpp.applied/3549503.1.aspx
Автор: Кодт
Дата: 27.09.09

Я плакал.... Сихронизация + копирование структуры. И после этого еще кто-то говорит что код на C++ быстрый?

PD>Я бы так сказал — язык С++ дает мне свободу делать все, что я хочу.

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

PD>А это неизбежно сопряжено с возможностью сделать и ошибку. Чтобы свободой как следует воспользоваться, надо хорошо понимать, что при этом можно, и что нельзя делать.

И чтобы не совершать ошибок надо или изобразить "закат солна вручную", или не пользоваться многими возможностями.
Например чтобы не поиметь утечек надо не возвращать указатель на объект из функции. Надо или отказаться от этой возможности и обрезать возможность применения многих паттернов, или считать ссылки, что получается медленно. Вот тебе и свобода.
Re[30]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 04:40
Оценка:
Здравствуйте, netch80, Вы писали:

PD>>Я вовсе не утверждаю, что С++ идеален. Более того, мне многое в нем не нравится. Но альтернативы-то пока нет.


N>Ada?


Хороший язык. Помню, еще тогда, в начале 80-х читал книгу с удовольствием. Но не пошел...

PD>> Скажу сразу — все управляемые языки в качестве альтернативы не предлагать, это не обсуждается.


N>"Сразу скажу" — не понимаю причин такого ограничения.


Не будем обсуждать.
With best regards
Pavel Dvorkin
Re[29]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 04:58
Оценка: -1 :)
Здравствуйте, gandjustas, Вы писали:

PD>>http://rsdn.ru/forum/cpp.applied/3549503.1.aspx
Автор: Кодт
Дата: 27.09.09

G>Я плакал.... Сихронизация + копирование структуры. И после этого еще кто-то говорит что код на C++ быстрый?

Я уже давно рыдать должен, читая твои опусы. Нет там копирования. Там просто КодТ ошибку допустил, что вполне простительно, так как это лишь псевдокод

Вместо

A data = &(A*)(pd->data)

должно быть

A* data = (A*) pd->data;

Впрочем, проще было бы вот так

struct PassData
{
event thanks; // ваш любимый инструментарий
A* data;
PassData(A* p) : data(p) {}
};

и тогда

A* data = pd->data;

Иди учи язык!
With best regards
Pavel Dvorkin
Re[30]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.10.09 05:09
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


PD>>>http://rsdn.ru/forum/cpp.applied/3549503.1.aspx
Автор: Кодт
Дата: 27.09.09

G>>Я плакал.... Сихронизация + копирование структуры. И после этого еще кто-то говорит что код на C++ быстрый?

PD>Я уже давно рыдать должен, читая твои опусы. Нет там копирования. Там просто КодТ ошибку допустил, что вполне простительно, так как это лишь псевдокод


PD>Вместо


PD> A data = &(A*)(pd->data)


PD>должно быть


PD> A* data = (A*) pd->data;


PD>Впрочем, проще было бы вот так


PD>struct PassData

PD>{
PD> event thanks; // ваш любимый инструментарий
PD> A* data;
PD> PassData(A* p) : data(p) {}
PD>};

PD>и тогда


PD> A* data = pd->data;


Да ты че?
Если ты не понял, то указатель A* data в потоке станет ни разу не валидным после завершения функции launch.

PD>Иди учи язык!

Иди учись программировать. Язык тут кстати совершенно не при чем.
Re[31]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 05:31
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Да ты че?

G>Если ты не понял, то указатель A* data в потоке станет ни разу не валидным после завершения функции launch.

О господи! Сил уже нет!

Ты хоть пойми, что такое рандеву! Внутри launch стоит wait_event(pd.thanks); А в потоковой функции есть set_event(pd->thanks);, который и означает, что работа с этим A* закончена.
До тех пор, пока не вызвали в потоке set_event(pd->thanks), переменная A a из основного потока существует, так как выход из launch невозможен.И как только дождемся в основном потоке — только теперь эта переменная может помереть.
With best regards
Pavel Dvorkin
Re[32]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.10.09 05:35
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


G>>Да ты че?

G>>Если ты не понял, то указатель A* data в потоке станет ни разу не валидным после завершения функции launch.

PD>О господи! Сил уже нет!

Главное чтобы мозг был.

PD>Ты хоть пойми, что такое рандеву! Внутри launch стоит wait_event(pd.thanks); А в потоковой функции есть set_event(pd->thanks);, который и означает, что работа с этим A* закончена.

PD>До тех пор, пока не вызвали в потоке set_event(pd->thanks), переменная A a из основного потока существует, так как выход из launch невозможен.И как только дождемся в основном потоке — только теперь эта переменная может помереть.
Дождемся чего? Завершения потока?
Иначе придется скопировать данные из A еще куда-либо.
Чудес не бывает.

Так что иди учись программировать.
Re[32]: пример
От: Pavel Dvorkin Россия  
Дата: 19.10.09 05:45
Оценка:
Наиболее типичное использование

основной поток

A a;
beginthreadex(&a);
while(не знаю что)
{
заполняем а
SignalObjectAndWait(...) // или пара SetEvent + WaitForSingleObject
}

то есть изготовили данные и просигнализировали второму потоку , что он должен обработать

второй поток

threadfunc(void* p)
{
A* pa = (A*) p;
while(не знаю что)
{
WaitForSingleObject;
// что-то делем c pa
// SetEvent
}
With best regards
Pavel Dvorkin
Re[33]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 05:48
Оценка:
Здравствуйте, gandjustas, Вы писали:

PD>>До тех пор, пока не вызвали в потоке set_event(pd->thanks), переменная A a из основного потока существует, так как выход из launch невозможен.И как только дождемся в основном потоке — только теперь эта переменная может помереть.

G>Дождемся чего? Завершения потока?

ивента! Ясно же написано — wait_event(pd.thanks);

G>Иначе придется скопировать данные из A еще куда-либо.


Зачем ? Они уже обработаны. Поток продолжает свою деятельность без обращения к ним.

G>Чудес не бывает.


Это точно. Бывает лишь элементарное непонимание!

См. пример http://rsdn.ru/forum/philosophy/3574170.1.aspx
Автор: Pavel Dvorkin
Дата: 19.10.09
With best regards
Pavel Dvorkin
Re[34]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.10.09 05:56
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

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


PD>>>До тех пор, пока не вызвали в потоке set_event(pd->thanks), переменная A a из основного потока существует, так как выход из launch невозможен.И как только дождемся в основном потоке — только теперь эта переменная может помереть.

G>>Дождемся чего? Завершения потока?
PD>ивента! Ясно же написано — wait_event(pd.thanks);

G>>Иначе придется скопировать данные из A еще куда-либо.

PD>Зачем ? Они уже обработаны. Поток продолжает свою деятельность без обращения к ним.

То есть на время обработки А (фактически где существует обращение к А) launch будет заблокирован?
Прекрасно!!! Особенно если вся работа потока сводится к A.


G>>Чудес не бывает.

PD>Это точно. Бывает лишь элементарное непонимание!
PD>См. пример http://rsdn.ru/forum/philosophy/3574170.1.aspx
Автор: Pavel Dvorkin
Дата: 19.10.09

Ты вообще представляешь как это медленно работать будет? Особенно если тебе в цикле надо раскидать работу по множеству потоков.
Re[35]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 06:02
Оценка:
G>Ты вообще представляешь как это медленно работать будет?

Ты вообще понимаешь, для чего SignalObjectAndWait сделана ? Кстати, ее добавили в NT 4.0, до этого ее не было.
With best regards
Pavel Dvorkin
Re[36]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.10.09 06:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

G>>Ты вообще представляешь как это медленно работать будет?


PD>Ты вообще понимаешь, для чего SignalObjectAndWait сделана ? Кстати, ее добавили в NT 4.0, до этого ее не было.


В курсе, только причем тут это?

Напиши таким образом реальный код, который например распараллеливает суммирование массива чисел на N потоков.
Re[35]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 06:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Особенно если тебе в цикле надо раскидать работу по множеству потоков.


На здоровье

основной поток

A a[N];
for (int i = 0; i < N; i++) beginthreadex(a+i);
while(не знаю что)
{
for (int i = 0; i < N; i++)
{
заполняем а[i]
SetEvent(hEventRequest[i])
}
WaitForMultipleObjects(N, hEventResponse, AND); // можно и что-то похитрее, например, ждать по OR, и как дождались — дать новое задание тому, кого дождались.
}


рабочие потоки

threadfunc(void* p)
{
A* pa = (A*) p;
while(не знаю что)
{
WaitForSingleObject;
// что-то делем c pa
SetEvent
}

А суть одна — что ни делай, переменная A (массив А) стать невалидной не может, так что указатели все тоже валидны.
With best regards
Pavel Dvorkin
Re[37]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 06:20
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Напиши таким образом реальный код, который например распараллеливает суммирование массива чисел на N потоков.


Да бога ради. Возьми мой пример

http://rsdn.ru/forum/philosophy/3574195.1.aspx
Автор: Pavel Dvorkin
Дата: 19.10.09


и замени в нем передачу в поток a+i на передачу адреса того участка, который нужно отсуммировать данному потоку, длины его и адрес переменной для суммы. Каждый поток получит свой кусок и найдет его сумму. Остается дождаться их всех и просуммировать суммы.

Кстати, это я делал примерно год назад, когда обсуждали вычисление сумм по столбцам или строкам пикселей окна.
With best regards
Pavel Dvorkin
Re[38]: ссылка с моим кодом и твоим ответом
От: Pavel Dvorkin Россия  
Дата: 19.10.09 06:36
Оценка:
http://www.rsdn.ru/forum/philosophy/3159884.1.aspx
Автор: gandjustas
Дата: 01.11.08


ну и далее по треду.
With best regards
Pavel Dvorkin
Re[32]: рандеву (Inline records)
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.10.09 07:11
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

G>>Да ты че?

G>>Если ты не понял, то указатель A* data в потоке станет ни разу не валидным после завершения функции launch.
PD>О господи! Сил уже нет!

Я поменял subject, а то мы далеко ушли от исходной темы.

PD>Ты хоть пойми, что такое рандеву! Внутри launch стоит wait_event(pd.thanks); А в потоковой функции есть set_event(pd->thanks);, который и означает, что работа с этим A* закончена.

PD>До тех пор, пока не вызвали в потоке set_event(pd->thanks), переменная A a из основного потока существует, так как выход из launch невозможен.И как только дождемся в основном потоке — только теперь эта переменная может помереть.

Это плохое решение по нескольким причинам:

1. Малейшая ошибка в реализации launch() в более-менее сложном случае (а не передаче одного параметра) приведёт к тому, что launch() может вылететь с исключением. В этом случае другие треды, уже запущенные, будут обращаться к невалидной памяти, соответственно с непредсказуемыми последствиями.

Поэтому твой код резко противоречит стилю C++, в котором надо сейчас писать так, чтобы вышибание табуретки из-под ног в любой момент не приводило к общему завису;))

2. Ты останавливаешь тот тред, в котором launch(), целиком внутри неуправляемой функции. Если тебе потребуется его прервать, ты не сможешь этого сделать.

3. Точно так же, ты не в состоянии отреагировать без внешних костылей на завис или слёт другого треда перед тем, как тебе сказали thanks (только не говори, что это невозможно;)) — будешь просто висеть в ожидании события.

Таким образом, твой метод, даже если работает, должен быть квалифицирован как немасштабируемый хак, пригодный только для тепличных случаев идеальной работы. Напротив, вариант аллоцировать каждому кусок устойчивой (по сравнению с твоим стеком) памяти и отдать в пользование (в идеале — да, чтобы тут же сказал free) не страдает подобными ограничениями.
The God is real, unless declared integer.
Re[35]: Inline records
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.10.09 07:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>Дождемся чего? Завершения потока?

PD>>ивента! Ясно же написано — wait_event(pd.thanks);
G>>>Иначе придется скопировать данные из A еще куда-либо.
PD>>Зачем ? Они уже обработаны. Поток продолжает свою деятельность без обращения к ним.
G>То есть на время обработки А (фактически где существует обращение к А) launch будет заблокирован?

Нет, смотри оригинальный пример. PD сейчас сам запутался. Изначально событие было занято только на момент, пока новый тред не подтвердил приём данных. Это более-менее обычный приём для ряда ситуаций. Сейчас же он пытается "на ходу" развернуть это на пул потоков с ожиданием любого из, а это уже совсем другая ситуация.

PD>>Это точно. Бывает лишь элементарное непонимание!

PD>>См. пример http://rsdn.ru/forum/philosophy/3574170.1.aspx
Автор: Pavel Dvorkin
Дата: 19.10.09

G>Ты вообще представляешь как это медленно работать будет? Особенно если тебе в цикле надо раскидать работу по множеству потоков.

Не так уж и медленно. Скорее наоборот — быстро:) Другой вопрос, что я не понимаю смысл применения системного вызова и пинания этим ядра там, где malloc/free сработают быстрее и не требуя (в общем случае) переключения контекста вверх.
The God is real, unless declared integer.
Re[7]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 19.10.09 07:45
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, kochetkov.vladimir, Вы писали:


VD>>>Ну, и поддерживается ли для типа record (если он существует) структурная идентичность? Ну, когда две независимо созданные записи идентичны при условии, что идентичны значения и имена полей.


KV>>Во втором варианте кода — да, поддерживается:


VD>Здорово!

VD>Но все же здесь получается, что возвращаются экземпляры класса. Идентичность для них достигается путем переопределения метода сравнения. Не получится ли так, что в некотором контексте объекты все же окажутся не равны?

Так это ж и есть тот самый способ, благодаря которым объекты прикидываются утками, когда надо Но получится обязательно , правда не из-за подхода, а в силу недоработанности кода:

1. Необходимо еще переопределить метод __ne__() (проверка на неравенство, для симметричности операции), либо вместо обоих один метод __cmp__() (сравнение)
2. Приведенные в примерах рекорды нельзя использовать в качестве ключей в словарях (хэш-таблицах), т.к. метод __hash__() они унаследуют от тупла, а следовательно у двух записей с одинаковыми значениями полей, но с разными названиями будет одинаковый хэш.

VD>ЗЫ


VD>А вообще, скрипты основанные на модели прототипного ООП и мета-классов весьма гибки в области метапрограммирования. Но платой за это является производительность. Ведь такие вот выкрутасы уже никак не оптимизируешь. Тут само представление объекта должно быть хэш-таблицей. Иначе фокус не пройдет.


Это да

VD>Я же рассуждал о статически типизированных языках в которых все определяется декларацией.

VD>Собственно создать такой перепаковщик можно и на базе макросов того же немерле. Проблема в том, что это не очень красивое решение, так как операция сравнения будет весьма не шустрой (придется сравнивать имена и значения полей в рантайме), и не всегда корректной, так как порой важен фактический тип, а не наличие у него операции сравнения. Скажем при передаче в качестве параметра.

В голове крутится мысль, что можно уйти от необходимости проверок в рантайме, если имена полей представлять не строкой, а одноименным типом, который будет генериться средствами мета-программирования, но возможно это — заблуждение от недостаточного владения немерлом, а не реальное решение

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[36]: Inline records
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.10.09 07:50
Оценка:
Здравствуйте, netch80, Вы писали:

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


G>>>>Дождемся чего? Завершения потока?

PD>>>ивента! Ясно же написано — wait_event(pd.thanks);
G>>>>Иначе придется скопировать данные из A еще куда-либо.
PD>>>Зачем ? Они уже обработаны. Поток продолжает свою деятельность без обращения к ним.
G>>То есть на время обработки А (фактически где существует обращение к А) launch будет заблокирован?

N>Нет, смотри оригинальный пример. PD сейчас сам запутался. Изначально событие было занято только на момент, пока новый тред не подтвердил приём данных. Это более-менее обычный приём для ряда ситуаций. Сейчас же он пытается "на ходу" развернуть это на пул потоков с ожиданием любого из, а это уже совсем другая ситуация.

А что значит "прием данных"?
Если использовать только указатель, то launch не должна завершаться пока поток работает с этими данными.
Если же данные копируются, то это лишние затраты.
Затраты конечно маленькие по сравнению с созданием потока, но общая картина от этого лучше не становится.
Re[8]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.10.09 11:45
Оценка: +1
Здравствуйте, kochetkov.vladimir, Вы писали:

KV>В голове крутится мысль, что можно уйти от необходимости проверок в рантайме, если имена полей представлять не строкой, а одноименным типом, который будет генериться средствами мета-программирования, но возможно это — заблуждение от недостаточного владения немерлом, а не реальное решение


С типом проблема в том, что в донете нет структурной идентичности. И утиной типизации тоже нет. Так что два, с виду, одинаковых объекта в некотором месте будут не совместимы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[33]: рандеву (Inline records)
От: Pavel Dvorkin Россия  
Дата: 19.10.09 12:30
Оценка:
Здравствуйте, netch80, Вы писали:

N>Я поменял subject, а то мы далеко ушли от исходной темы.


Честно говоря, мне давно уже надоела вся эта дискуссия. Черт меня угораздил ввязаться в нее. Началось все с безобидного (как мне казалось тогда) ответа samius

http://rsdn.ru/forum/philosophy/3571479.1.aspx
Автор: Pavel Dvorkin
Дата: 16.10.09


где я вымолвил слово в защиту ООП для автоматических переменных, дав кусок из стандарта С++. А дальше в дело вмешался gandjustas, а я , вместо того, чтобы просто проигнорировать его опус, решил ему эту ссылку на стандарт привести, ну и пришлось в итоге доказывать, что Волга впадает в Каспийское море


N>Это плохое решение по нескольким причинам:


Это не плохое и не хорошее решение. Это (сообщение КодТ) вообще не решение, а просто псевдокод, иллюстрирующий тот факт, что автоматические переменные вполне можно передавать куда угодно и как угодно, брать на них указатели и передавать их куда угодно и как угодно. Одно лишь требование должно выполняться — указатель не должен жить (или по крайней мере использоваться) дольше , чем то, на что он показывает. И только. И правило это применимо не только к автоматическим объектам, но и к объектам, выделяемым в куче.

В этом смысле вот такой код

int main()
{
char a[100], *p = a;
// делай с a и p что хочешь, передавай их хоть в потоки, хоть куда угодно.
// если запустили потоки — дождаться завершения их всех
}


совершенно корректен, потому что a и p имеют одинаковое время жизни, иными словами, пока не выйдем на } в main, массив жив и указатели на него валидны.

Равным образом можешь придумать примеры, где это будет неверно.

И именно для демонстрации этого приницпа и был написан КодТ этот пример. Для этого, и ничего больше.

А стоит ли делать именно так или же не стоит — от задачи зависит. Где-то стоит так, где-то — иначе.

Вот, собственно , и все. А обсуждать плюсы или минусы этого конкретного примера я не буду, так как к исходному вопросу это отношения не имеет, а дискутировать специально на эту тему я не хочу. Да и не автор я этого примера. КодТ просто показал, что такое возможно. Больше ничего.

N>1. Малейшая ошибка в реализации launch() в более-менее сложном случае (а не передаче одного параметра) приведёт к тому, что launch() может вылететь с исключением. В этом случае другие треды, уже запущенные, будут обращаться к невалидной памяти, соответственно с непредсказуемыми последствиями.

N>Поэтому твой код резко противоречит стилю C++, в котором надо сейчас писать так, чтобы вышибание табуретки из-под ног в любой момент не приводило к общему завису)

Без комментариев, ибо длинная это песня.

N>2. Ты останавливаешь тот тред, в котором launch(), целиком внутри неуправляемой функции. Если тебе потребуется его прервать, ты не сможешь этого сделать.


Во-первых, у нас все функции неуправляемые А во-вторых, посмотри мой пример с SignalObjectAndWait. Внутри нее мне прерывать совершенно незачем.

N>3. Точно так же, ты не в состоянии отреагировать без внешних костылей на завис или слёт другого треда перед тем, как тебе сказали thanks (только не говори, что это невозможно) — будешь просто висеть в ожидании события.


Верно, но какое отношение это имеет к передаче автоматических переменных ? Решений много, обсуждать сейчас не буду.

N>Таким образом, твой метод, даже если работает, должен быть квалифицирован как немасштабируемый хак


Не согласен, так как здесь нет никаких действий, противоречащих стандарту языка.


>пригодный только для тепличных случаев идеальной работы. Напротив, вариант аллоцировать каждому кусок устойчивой (по сравнению с твоим стеком) памяти и отдать в пользование (в идеале — да, чтобы тут же сказал free) не страдает подобными ограничениями.


Я что-то не понял, почему это решит проблемы 3 или 2 ? Да и 1 тоже. Я ведь с таким же успехом могу по ошибке в launch освободить их раньше, чем это будет надо.

Вот сравни

int main()
{

int a[10][20];

потокам передаем свои строки, т.е. a[i] из стека
ждем завершения всех потоков
}

и

int main()
{

int *a[10];
for (int i = 0; i < 10; i++)
a[i] = new int[10];
// ты уж мне прости автоматический массив указателей, но передаю я все же указатель на память, выделенную в куче. Если простить не можешь — помести их в другое место, но оставь их 10 штук
потокам передаем свои строки, т.е. a[i] из кучи.
ждем завершения всех потоков
}

В чем разница ? И почему в первом случае память неустойчива, а во втором устойчива ? Скорее наоборот, ошибку сделать легче

int main()
{

int *a[10];
for (int i = 0; i < 10; i++)
a[i] = new int[10];

потокам передаем свои строки, т.е. a[i] из кучи.
и не нужны мне вроде бы эти массивы больше...
for (int i = 0; i < 10; i++)
delete a[i];
ждем завершения
}

И вот она готова! Попробуй эту ошибку в первом примере сделать ! То есть попробуй сделать хоть один a[i] невалидным.
With best regards
Pavel Dvorkin
Re[36]: Inline records
От: Pavel Dvorkin Россия  
Дата: 19.10.09 12:49
Оценка:
Здравствуйте, netch80, Вы писали:

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


N>Нет, смотри оригинальный пример. PD сейчас сам запутался.


Отчасти да. Увы, КодТ сделал там ошибку, из-за нее все и пошло

A data = &(A*)(pd->data)

Так быть не может. Либо

A data = *(A*)(pd->data)

и тогда копируем, верно, и ты прав

>Изначально событие было занято только на момент, пока новый тред не подтвердил приём данных. (т.е скопировал — PD)


либо

A* data = (A*)(pd->data)

как модифицировал я, и тогда надо быстро обработать data, и просигнализировать, что он мне больше не нужен (что я и предложил).

Оба решения корректны.



>Это более-менее обычный приём для ряда ситуаций. Сейчас же он пытается "на ходу" развернуть это на пул потоков с ожиданием любого из, а это уже совсем другая ситуация.


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

N>Не так уж и медленно. Скорее наоборот — быстро Другой вопрос, что я не понимаю смысл применения системного вызова и пинания этим ядра там, где malloc/free сработают быстрее и не требуя (в общем случае) переключения контекста вверх.


Что-то просто не понял. Извини, но после десяти часов занятий (сегодня) я уже плохо соображаю. Я предлагаю массив в стеке. Ты предлагаешь выделить память в куче. В этом я разницу вижу. Дальше я плохо понимаю. Быстрее чего malloc/free сработает — выделения в стеке ? Куда и какой контекст я переключаю, а ты нет ?
With best regards
Pavel Dvorkin
Re[20]: Inline records
От: Трурль  
Дата: 20.10.09 14:30
Оценка: 10 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

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


S>>Скорее

S>>
S>>POINTS points = MakePoints(10);
S>>POINT p = GetPoint(points, 10);
S>>

S>>При такой инкапсуляции мы можем потерять размер записи. Здесть POINTS и POINT — это лишь хэндлы.

PD>Если POINT и POINTS — это только хендлы — пожалуйста, дай мне их описание. typedef или #define. И ты сразу поймешь, что кроме void* у тебя ничего нет — типов-то нет. И никто мне не помешает написать


PD>points = p;


PD>и даже предупреждения не будет, а потом все с ума сойдут.


typedef struct POINT* POINT;
typedef struct POINTS* POINTS;
POINTS MakePoints(int);
POINT GetPoint(POINTS, int);
void BadUasge()
{
  POINTS points = MakePoints(10);
  POINT p = GetPoint(points, 10);
  points = p;
}


warning C4133: '=' : incompatible types — from 'struct POINT *' to 'struct POINTS *'

Re[21]: Inline records
От: Pavel Dvorkin Россия  
Дата: 21.10.09 05:14
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>
Т>typedef struct POINT* POINT;
Т>typedef struct POINTS* POINTS;
Т>POINTS MakePoints(int);
Т>POINT GetPoint(POINTS, int);
Т>void BadUasge()
Т>{
Т>  POINTS points = MakePoints(10);
Т>  POINT p = GetPoint(points, 10);
Т>  points = p;
Т>}
Т>



1>e:\9869\1111.cpp(1) : error C2040: 'POINT' : 'POINT *' differs in levels of indirection from 'POINT'

1>e:\9869\1111.cpp(2) : error C2040: 'POINTS' : 'POINTS *' differs in levels of indirection from 'POINTS'
1>e:\9869\1111.cpp(7) : error C2079: 'points' uses undefined struct 'POINTS'
1>e:\9869\1111.cpp(7) : error C2440: 'initializing' : cannot convert from 'POINTS' to 'int'
1> Source or target has incomplete type
1>e:\9869\1111.cpp(8) : error C2079: 'p' uses undefined struct 'POINT'
1>e:\9869\1111.cpp(8) : error C2664: 'GetPoint' : cannot convert parameter 1 from 'int' to 'POINTS'
1> Source or target has incomplete type
1>Build log was saved at "file://e:\9869\Debug\BuildLog.htm"
1>9869 — 6 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
With best regards
Pavel Dvorkin
Re[22]: Inline records
От: Трурль  
Дата: 21.10.09 09:03
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

>1>e:\9869\1111.cpp(1) : error C2040: 'POINT' : 'POINT *' differs in levels of indirection from 'POINT'


Вроде, речь шла о си.
Re[23]: Inline records
От: Pavel Dvorkin Россия  
Дата: 21.10.09 09:16
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Здравствуйте, Pavel Dvorkin, Вы писали:


>>1>e:\9869\1111.cpp(1) : error C2040: 'POINT' : 'POINT *' differs in levels of indirection from 'POINT'


Т>Вроде, речь шла о си.


Сорри. Верно.

Ты сделал примерно то же, что в режиме #define STRICT делается в windows.h. Хендлы в теории — это просто целые числа, но если их реализовать как unsigned int (или long), то можно будет писать hPen = hBrush. Поэтому они там реализованы как указатели на структуры разных типов.
With best regards
Pavel Dvorkin
Re[2]: Inline records
От: kochetkov.vladimir Россия https://kochetkov.github.io
Дата: 24.10.09 23:12
Оценка:
Здравствуйте, samius, Вы писали:

S>так принято записывать только потому что string->int->Y еще больше не соответсвует действительности.


Нет, не поэтому, а потому что "string*int" читается как "элемент множества, являющегося декартовым произведением множеств значений типов string и int". А элементом декартового произведения двух множеств как раз и является кортеж (в терминологии теории множеств) с парой элементов, принадлежащих каждый своему множеству значений.

[Интервью] .NET Security — это просто
Автор: kochetkov.vladimir
Дата: 07.11.17
Re[3]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.10.09 05:19
Оценка:
Здравствуйте, kochetkov.vladimir, Вы писали:

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


S>>так принято записывать только потому что string->int->Y еще больше не соответсвует действительности.


KV>Нет, не поэтому, а потому что "string*int" читается как "элемент множества, являющегося декартовым произведением множеств значений типов string и int". А элементом декартового произведения двух множеств как раз и является кортеж (в терминологии теории множеств) с парой элементов, принадлежащих каждый своему множеству значений.


Про декартово произведение я знаю, не знал лишь что Nemerle может подать кортеж в метод, где параметры объявлены через запятую. F# вот так не может.
Re[10]: Inline records
От: vdimas Россия  
Дата: 25.11.09 12:31
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Выгода использования таплов (при малом количестве полей) проявляется в том, что паттерн получается очень компактным, создание значения тоже, плюс (но это и у записей) — нам не нужно создавать дополнительный тип.


Ну дык есть же концепция анонимных типов в C#. Решают ровно ту же задачу, что и таплы — избавляются от описания дополнительных типов.


L>Очевидно, что в критерии "полей достаточно мало для тапла" есть немного субъективного, т.к. понимание кода вещь тоже субъективная. По моему это и есть место для спора


Да нечего тут спорить. Самодокументируемость кода — это всегда +, хоть с одной переменной, хоть с двумя. Ф-ия, возвращающая 1 результат обычно описывает его своим именем. А если результатов более одного, то надо как-то это имя "раскидать" по ним, что не всегда выливается в разумный баланс м/у эстетичностью имени и однозначностью понимания читателем кода. Даже в случае всего 2-х возвращаемых значений.


V>>По твоему имена аргументов методов хранятся в метаинформации того же дотнет просто так? Ведь можно было бы обойтись хранением в метаинформации только типов (как в obj-файлах C++), а сигнатуру давать в виде хелпа.


L>Я не знаю дотнет. Ты про IDE говоришь в выделенном?


Это было утрирование, на самом деле в дотнете в метаинформации хранится не только сигнатура ф-ии (как в obj-файлах), но и имена аргументов. Ну как хотел бы это видеть примерно показал здесь: http://www.rsdn.ru/forum/philosophy/3571316.1.aspx
Автор: vdimas
Дата: 16.10.09

Ибо в дотнете большая засада в том, что анонимные типы сегодня не являются таплами, т.е. не совместимы м/у собой.


V>>Там где 2-3 поля тупла, вероятность ошибки не высока... но ведь очевидно, что чем больше полей, тем больше требуется удобств.


L>Разумеется, не стоит пользоваться таплами с надцатью полями.


Даже с 2-мя полями, повторюсь, разумнее их именовать. Иначе мы получаем вроде бы "математически минималистическую" запись, которую тут пропагандирует известный хаскелист. Однако, при чтении и поддержке кода практически всегда пофиг суть решения (серьезно), гораздо важнее понимания задачи, которая решается этим кодом. Простым умножением матриц можно решить чуть ли не бесчисленное м-во задач, однако в коде необходимо указывать, какая именно из них решается. Я одно время довольно много просматривал исходников на Хаскеле (из любопытства). И что заметил: пояснения к коду приходилось искать в месте его "клиентского" использования, когда уже конкретным переменным в позициях тапла давались осмысленные имена. Т.е. без этих примеров использования самодокументация кода практически стремится к 0-лю, поэтому с т.з. моего ИМХО тут и нечего обсуждать.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[2]: Inline records
От: Alexander Polyakov  
Дата: 28.11.09 20:39
Оценка:
Здравствуйте, Undying, Вы писали:

U>Плюс это решение можно применять не только для возвращения нескольких значений из функции, но и для куда более широкого круга задач связанных с необходимостью переименования полей/методов класса, т.е. решение было бы куда более мощным и в отличие от туплов кардинально расширяющим возможности языка.

Тут вот какой момент интересен. В Tuple-ах (в C# 4.0) “поля” Item1, Item2 …, ItemN равнозначны. Поскольку они равнозначны, то отсутствие у них нормальных имен вполне естественно (Item1, …, ItemN это, конечно, не нормальные имена, а так затычки). Соответственно, требуется механизм для введения нормальных имен в момент использования Tuple-ов. А вот представить класс с неравнозначными свойствами/методами и чтобы для него потребовалось переименование этих свойств/методов -- мне такое представить сложно. Ты можешь привести реальный пример, где такое требуется? Когда свойства/методы неравнозначны, то они наверняка уже имеют говорящие имена. Иначе как-то странно, свойство имеет некоторый смысл отличный от других свойств, но не имеет нормального имени. А если имена и так уже говорящие, тогда зачем их переименовывать? (см. Примечание 1.)

С другой стороны предложенный тобой способ для задачи “дать имена и типы полям Tuple-а” не является самым удобным.
class MyReturnValue implement Tuple<string, DateTime, Item1 as Name, Item2 as BeginTime>;
Плохо прослеживается, какой тип к какому имени относится. Это происходит из-за того, что тип с именем связывается через посредника Item1, ..., ItemN. Я бы предпочел что-то типа такого
class MyReturnValue implement Tuple
{
    string Name;
    DateTime BeginTime;
}
Правда обычный Tuple в смысле C# 4.0 тут не подойдет. Но я высказал критику с точки зрения пользователя, а над реализацией уже можно думать (варианты есть).

Примечание 1. На самом деле, в одном классе могут существовать несколько групп равнозначных свойств, например, SmallItem1, …, SmallItemN и BigItem1, …, BigItemN. Это, да, такое можно представить, но чтобы все свойства были неравнозначны и не имели нормальных имен -- такое представить сложно.
Re[11]: Inline records
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 29.12.09 21:06
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ибо в дотнете большая засада в том, что анонимные типы сегодня не являются таплами, т.е. не совместимы м/у собой.


Совместимы. Но только в пределах одной сборки.
... << RSDN@Home 1.2.0 alpha 4 rev. 1324 on Windows 7 6.1.7600.0>>
AVK Blog
Re[11]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 30.12.09 07:30
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Даже с 2-мя полями, повторюсь, разумнее их именовать. Иначе мы получаем вроде бы "математически минималистическую" запись, которую тут пропагандирует известный хаскелист. Однако, при чтении и поддержке кода практически всегда пофиг суть решения (серьезно), гораздо важнее понимания задачи, которая решается этим кодом. Простым умножением матриц можно решить чуть ли не бесчисленное м-во задач, однако в коде необходимо указывать, какая именно из них решается. Я одно время довольно много просматривал исходников на Хаскеле (из любопытства). И что заметил: пояснения к коду приходилось искать в месте его "клиентского" использования, когда уже конкретным переменным в позициях тапла давались осмысленные имена. Т.е. без этих примеров использования самодокументация кода практически стремится к 0-лю, поэтому с т.з. моего ИМХО тут и нечего обсуждать.


Я понял твою мысль. И скорее согласен, но случаи разные бывают. В том же Хаскеле переменные/параметры гораздо менее важны, чем в С#, а типы более важны, потому что выразить через них можно (удобнее) больше. Поэтому таплы там к месту — имена вполне заменяют типы.

http://www.haskell.org/hoogle/?hoogle=a+-%3E+%28b%2Cc%29

По типам сразу видно что где возвращается. Там, где типы одинаковые, всё равно ясно из контекста — это или split (span и т.д.), т.е. делим на левое и правое, или диапазон (genRange), т.е. начало и конец. Не нужны там имена для параметров.
Re[4]: Inline records
От: Kluge  
Дата: 04.01.10 15:41
Оценка: 7 (1)
Здравствуйте, samius, Вы писали:

S>Про декартово произведение я знаю, не знал лишь что Nemerle может подать кортеж в метод, где параметры объявлены через запятую. F# вот так не может.


let f (a, b) = a+b;;
let p = 3,5;;
f p;;

Это работает. А как F# неможет?
Лоботомию в массы! (с)
Re[5]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 04.01.10 15:48
Оценка:
Здравствуйте, Kluge, Вы писали:

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


S>>Про декартово произведение я знаю, не знал лишь что Nemerle может подать кортеж в метод, где параметры объявлены через запятую. F# вот так не может.


K>Это работает. А как F# неможет?


Извиняюсь, ляпнул не проверив.
Для меня это неожиданность. Спасибо!
Re[5]: Inline records
От: Kluge  
Дата: 04.01.10 15:48
Оценка:
Здравствуйте, FR, Вы писали:

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


S>>Да, в F#-е эти туплы хоть и имеют должную поддержку языка, но вызов метода с передачей кортежа вместо аргументов через запятую (как в немерле) и там не реален. Потому то меня и смутила форма записи типа кортежа со звездой приминительно к немерле (я ожидал от его кортежей чего-то подобного F#-у).


FR>В F# вызов функций с несколькими аргументами это просто сахар для карринга, поэтому никакой "совместимости" и не может быть. Но никто не запрещает (хотя это плохой стиль) использовать вместо функций с несколькими аргументами функцию с одним аргументом кортежом, тогда все будет отлично совместимо.


ИМХО лучше использовать сахар раз-уж он есть и если нужно передать (int * int) в int -> int -> 'a то использовать <|| & ||> вместо того, что-бы менять тип параметра на кортеж.
Лоботомию в массы! (с)
Re[5]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 04.01.10 19:25
Оценка:
Здравствуйте, Kluge, Вы писали:

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


S>>Про декартово произведение я знаю, не знал лишь что Nemerle может подать кортеж в метод, где параметры объявлены через запятую. F# вот так не может.


K>
K>let f (a, b) = a+b;;
K>let p = 3,5;;
K>f p;;
K>

K>Это работает. А как F# неможет?

Точнее я полагал что так не получится:
open System.Collections.Generic
let p = (3,5)
let d = new Dictionary<_,_>()
d.Add(p)

Но проблем не возникло и с этим.
Re[6]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 04.01.10 19:28
Оценка:
Здравствуйте, Kluge, Вы писали:

K>ИМХО лучше использовать сахар раз-уж он есть и если нужно передать (int * int) в int -> int -> 'a то использовать <|| & ||> вместо того, что-бы менять тип параметра на кортеж.


А как насчет |||> или ||||> ?
Re: Inline records
От: vdimas Россия  
Дата: 05.01.10 03:11
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Расширение C#:

VD>
VD>(string ret1) FuncName(int param2)
VD>{
VD>}
VD>


Для самого распространенного случая, когда возвращаемый параметр один, обычно имя ф-ии именно его и описывает.
Re[12]: Inline records
От: vdimas Россия  
Дата: 05.01.10 03:27
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Совместимы. Но только в пределах одной сборки.


Может модуля?
Сдается мне, они совместимы в пределах одной сессии компилятора, который способен сгенерить один анонимный тип на все "совместимые" случаи.
Re[7]: Inline records
От: Kluge  
Дата: 05.01.10 10:40
Оценка:
Здравствуйте, samius, Вы писали:

S>А как насчет |||> или ||||> ?


В библиотеке есть до 3х, но если очень нужно можно сделать хоть до 18ти и дальше
Но в моей практике если есть кортеж, то это значения связанные смыслом, а значит и функция принимает в качестве параметра кортеж.
Если мы использовали кортеж для возвращения нескольких, несвязанных, значений то лучше сразу сделать декомпозицию и передавать дальше как два значения.
Лоботомию в массы! (с)
Re[2]: Inline records
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.01.10 14:47
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Для самого распространенного случая, когда возвращаемый параметр один, обычно имя ф-ии именно его и описывает.


Скажем если функция называется GetValue, то использоватьего в качестве названия параметра будет не хорошо.

К томуже функция сама по себе является значением которым иможно манипулировать.
Так что идея не очень хорошая.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Inline records
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 05.01.10 14:51
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Может модуля?


Ну, если модули раздельно компилируются, то может и модуля.

V>Сдается мне, они совместимы в пределах одной сессии компилятора


Нет такого понятия — сессия компилятора.
... << RSDN@Home 1.2.0 alpha 4 rev. 1335 on Windows 7 6.1.7600.0>>
AVK Blog
Re[8]: Inline records
От: Temoto  
Дата: 06.01.10 21:10
Оценка:
Здравствуйте, thesz, Вы писали:

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


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


T>>>Видишь ли, эти самые безымянные записи с именованными полями нужны ровно для того, чтобы не перепутать startTime и endTime. Поддержать инвариант startTime <= endTime. Сделать их "типы" "зависящими" от их значений. Зависимыми, иными словами.

VD>>Инвариантом здесь и не пахнет. Не надо домысливать.
VD>>Все это нужно чтобы помочь человеку. В первую очередь — это возможность доступа к полям по имени. Скажем, если у нас есть некий запрос возвращающий список кортежей, то куда удобнее обращаться к полям кортежей по именам, а не через позиции.

T>Или по сравнению с образцом.


T>>>Если смотреть на это с такой точки зрения, то всё это обсуждение яйца выеденного не стоит.

VD>>Не очень понятно с чего бы это, но на то они и разные точки зрения.

T>Всё, что ты делаешь (и я делаю, и кто-либо ещё делает в районе проектирования любых ЯП), направлено на уменьшение количества ошибок, допускаемых программистами.


T>Это единственная разумная точка зрения. "Мы все уменьшаем количество ошибок наших пользователей".


T>Далее идёт оценка размена — какие ошибки мы уменьшаем, какие увеличиваем. Где мы потратим больше времени, где меньше — второй вариант оценки.


T>Так вот, с этой точки зрения обсуждение обращения по именами полей безымянных структур выглядит разменом даже не самих пешек, а разменом пыли с тех частей доски, на которых эти пешки стояли.


Сергей, разъясните, пожалуйста.

В хаскеле советуют вместо туплов создавать отдельные типы. Ну и вот, создам я, например,

data FIO = FIO String String String


И потом, принимая тип FIO,

fio_pupkin = FIO "Пупкин" "Василий" "Апполинариевич"

print_first_name :: FIO -> IO ()


вместо (казалось бы, логичного) обращения к конкретной части типа,

-- вымысел
data FIO = FIO last:String first:String middle:String

print_first_name fio = print fio.first


будет паттерн-матчинг, который учитывает все, даже не используемые части.

print_first_name (FIO _, fname, _) = print fname


По-вашему, это пыль где пешки стояли?

T>А вот наличие имён для индикации инвариантов уже более интересно. Про наличие инвариантов и говорить отдельно не стоит, и так понятно, что это круто.


T>>>В Хаскель не попадают идеи из C#, в отличии от противоположного направления.

VD>>Возможно тебя это рассторит, но подовляющему большинству программистов плевать на хаскель и на то что в него попадает.
VD>>И плевать, на то что ты не можешь пройти мимо любой темы, посвященной дизайну ЯП, где забыли упомянуть Наскель.

T>Я так думаю, что это их проблемы, не мои.


T>Это они не знакомы с Хаскелем и потому, начиная говорить о функциональных языках программирования, сворачивают на то, что им известно.


T>>>Поэтому выгодней учить Хаскель. Что я и делаю.

VD>>Ну, учи. Что к другим то приставать?

T>Я альтруист.


VD>>Мне Hasskel без надобнсоти. Что на нем можно сделать? Чистые вычисления меня не интересуют.


T>А зря.


VD>>От иероглифической записи меня не прем (иначе я наверно перся бы от K или J). Мне нужен инструмент позволяющий решать проблемы реального мира.


T>Это любой ЯП общего назначения.


T>Интересно, ты не упомянул "наиболее эффективным образом".


T>>>Но ты спрашивай, я объясню.

VD>>Мне тебя не о чем спрашивать. Твоя позиция почти всегда деструктривно. Потому с тобой просто не интересно разговаривать. Вместо высказывания интересный мыслей ты постоянно выёживашся и потнушся.
VD>>Научись воспринимать окружающих как равных. Объяснять свою точку без понтов, воспринимать чужую точку зрения. Тогда с тобой будет интересно вести беседы. А так одно раздражения от общения с тобой получаешь.

T>Ух ты!


T>Это же комплимент.


T>Поскольку я слышу это от человека, которому последние два параграфа говорят гораздо чаще, чем мне.
Re[9]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 07.01.10 04:44
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Сергей, разъясните, пожалуйста.


Я, конечно, не thesz. И не за него. Но встряну тем не менее.

T>вместо (казалось бы, логичного) обращения к конкретной части типа,


T>
T>-- вымысел
T>data FIO = FIO last:String first:String middle:String

T>print_first_name fio = print fio.first
T>


Этот вымысел не далек от реального положения вещей:

-- реалии
data FIO = FIO { last :: String
               , first :: String
               , middle :: String
               }
print_first_name fio = print (first fio)
-- или
print_first_name = print . first


T>будет паттерн-матчинг, который учитывает все, даже не используемые части.


T>
T>print_first_name (FIO _, fname, _) = print fname
T>

Может запятые лишние?

Да, паттерн матчинг будет иметь место. Record syntax дешугарится в набор функций аля
first (FIO _ first _) = first


Но разве он учитывает все? Только то что используется.

T>По-вашему, это пыль где пешки стояли?


Речь ведь идет об fio.first vs first fio? Если да, то введем forward application

(|>) :: a -> (a -> b) -> b
x |> f = f x

-- тогда
print_first_name fio = fio |> first |> print


fio|>first ведь не сильно хуже чем fio.first?
Потому — да, пыль.
Re[9]: Inline records
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 07.01.10 11:27
Оценка:
Здравствуйте, Temoto, Вы писали:

Следует сокращать объемы цитирования.
... << RSDN@Home 1.2.0 alpha 4 rev. 1335 on Windows 7 6.1.7600.0>>
AVK Blog
Re[10]: Inline records
От: Temoto  
Дата: 07.01.10 13:39
Оценка:
S>Этот вымысел не далек от реального положения вещей:

S>-- реалии
S>data FIO = FIO { last :: String
S>               , first :: String
S>               , middle :: String
S>               }
S>print_first_name fio = print (first fio)
S>-- или
S>print_first_name = print . first


Это рекорды, которые, как известно, в хаскеле глобальные на модуль, соответственно указанный выше код на самом деле создаёт функцию

first :: FIO -> String


что, конечно, здорово, но нельзя сделать FIO{last::String, first...etc} И MyList a{last::a, first::a,...etc} в одном модуле.

T>>print_first_name (FIO _, fname, _) = print fname

S>Может запятые лишние?

Да, попутал, запятые лишние.

S>Да, паттерн матчинг будет иметь место. Record syntax дешугарится в набор функций аля

S>first (FIO _ first _) = first


S>Но разве он учитывает все? Только то что используется.


Нет, я использую только first, а должен писать "_" на каждую часть типа FIO, которую я *не использую*. <-- Внимание, плохо пахнет.

T>>По-вашему, это пыль где пешки стояли?


S>Речь ведь идет об fio.first vs first fio? Если да, то введем forward application


S>(|>) :: a -> (a -> b) -> b
S>x |> f = f x

S>-- тогда
S>print_first_name fio = fio |> first |> print


S>fio|>first ведь не сильно хуже чем fio.first?

S>Потому — да, пыль.

Порядок записи — пыль. first в глобальном namespace вместо namespace FIO — не пыль, а проблема.
Re[11]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 07.01.10 16:50
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Это рекорды, которые, как известно, в хаскеле глобальные на модуль, соответственно указанный выше код на самом деле создаёт функцию


T>
first :: FIO -> String


T>что, конечно, здорово, но нельзя сделать FIO{last::String, first...etc} И MyList a{last::a, first::a,...etc} в одном модуле.


Разве это критично?

T>Да, попутал, запятые лишние.


S>>Да, паттерн матчинг будет иметь место. Record syntax дешугарится в набор функций аля

T>
S>>first (FIO _ first _) = first
T>


S>>Но разве он учитывает все? Только то что используется.


T>Нет, я использую только first, а должен писать "_" на каждую часть типа FIO, которую я *не использую*. <-- Внимание, плохо пахнет.


В случае record syntax пишет компилятор, полностью освобождая от необходимости подсчета позиций полей.

S>>fio|>first ведь не сильно хуже чем fio.first?

S>>Потому — да, пыль.

T>Порядок записи — пыль. first в глобальном namespace вместо namespace FIO — не пыль, а проблема.


И ее решают с помощью модулей. Нет?
Глобальный namespace — вообще отдельная проблема. И в других языках ее решают по-разному. Например в C# — любые объявления кроме namespace-ов и delegate-ов могут быть только внутри классов. В будущих версиях F# глобальные определения тоже частично запретят (Warning: Files in libraries or multiple-file applications should begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'. This will be required in a future version of F#)
Re[12]: Inline records
От: Temoto  
Дата: 07.01.10 20:30
Оценка:
T>>Порядок записи — пыль. first в глобальном namespace вместо namespace FIO — не пыль, а проблема.

S>И ее решают с помощью модулей. Нет?


Это лечение симптомов, а не проблемы. "Костыль".
Re[13]: Inline records
От: samius Япония http://sams-tricks.blogspot.com
Дата: 08.01.10 05:46
Оценка:
Здравствуйте, Temoto, Вы писали:

T>>>Порядок записи — пыль. first в глобальном namespace вместо namespace FIO — не пыль, а проблема.


S>>И ее решают с помощью модулей. Нет?


T>Это лечение симптомов, а не проблемы. "Костыль".


Предлагаю определиться. Если выделенное — проблема, то лечится она модулями + import qualified. Если выделенное — симптом, то что же на самом деле является проблемой?
Может быть проблемой является отсутствие перегрузки функций в стиле С++, чтобы можно было определять рядом
first (FIO _ first _) = first
first (MyList _ first _) = first

?
Re[14]: Inline records
От: Temoto  
Дата: 08.01.10 10:30
Оценка:
T>>>>Порядок записи — пыль. first в глобальном namespace вместо namespace FIO — не пыль, а проблема.

S>>>И ее решают с помощью модулей. Нет?


T>>Это лечение симптомов, а не проблемы. "Костыль".


S>Предлагаю определиться. Если выделенное — проблема, то лечится она модулями + import qualified. Если выделенное — симптом, то что же на самом деле является проблемой?

S>Может быть проблемой является отсутствие перегрузки функций в стиле С++, чтобы можно было определять рядом
S>
S>first (FIO _ first _) = first
S>first (MyList _ first _) = first
S>

S>?

Да, выделенное — проблема. Модули+import это костыль. Лечение проблемы состояло бы в возможности определить несколько типов с одинаковыми названиями частей в одном модуле. Например, FIO.first.

Хотя, сейчас с утра мне это уже не кажется так чтоб уж очень большой проблемой.
Re[13]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 11.01.10 11:18
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Это лечение симптомов, а не проблемы. "Костыль".


А у меня вот не получается в одном неймспейсе определить несколько классов с одним именем. Приходится по разным разносить — это костыль?
Re[11]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 11.01.10 11:21
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Нет, я использую только first, а должен писать "_" на каждую часть типа FIO, которую я *не использую*. <-- Внимание, плохо пахнет.


foo ComplexRecord{recField=var} = doSomethingWith var
Re[14]: Inline records
От: Temoto  
Дата: 11.01.10 13:55
Оценка:
T>>Это лечение симптомов, а не проблемы. "Костыль".
L>А у меня вот не получается в одном неймспейсе определить несколько классов с одним именем. Приходится по разным разносить — это костыль?

Это толстый тролль. Тут даже отвечать ничего не надо.

Может быть, по-вашему, несколько одинаковых имён для частей типа (record syntax) в разных типах это нонсенс, вы так не пишете и разнести по разным модулям (только из-за ограничений языка) — не чуждо вашей природе. Моей чуждо. Это что-то типа могут ли люди с полностью одинаковыми ФИО сесть в одно такси. Редко встречается и когда произойдёт, они просто сядут в разные, никаких проблем. Но осадок остаётся, логики не видно.
Re[12]: Inline records
От: Temoto  
Дата: 11.01.10 13:58
Оценка:
T>>Нет, я использую только first, а должен писать "_" на каждую часть типа FIO, которую я *не использую*. <-- Внимание, плохо пахнет.

L>
L>foo ComplexRecord{recField=var} = doSomethingWith var
L>


Да с рекордами, кроме уникальности полей всё хорошо, они проблему нескольких "_" решают и просто foo = doSomethingWith $ recField.
Re[15]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 12.01.10 14:14
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Может быть, по-вашему, несколько одинаковых имён для частей типа (record syntax) в разных типах это нонсенс, вы так не пишете и разнести по разным модулям (только из-за ограничений языка) — не чуждо вашей природе. Моей чуждо. Это что-то типа могут ли люди с полностью одинаковыми ФИО сесть в одно такси. Редко встречается и когда произойдёт, они просто сядут в разные, никаких проблем. Но осадок остаётся, логики не видно.


Это не "часть типа" (record syntax — всего лишь сахар), а полноценная функция. В Haskell строгая типизация, для ad-hoc полиморфизма есть классы типов. Моя аналогия с классами подчёркивает именно этот факт. Что ты пытаешься сказать своей?
Re[16]: Inline records
От: Temoto  
Дата: 12.01.10 14:54
Оценка:
T>>Может быть, по-вашему, несколько одинаковых имён для частей типа (record syntax) в разных типах это нонсенс, вы так не пишете и разнести по разным модулям (только из-за ограничений языка) — не чуждо вашей природе. Моей чуждо. Это что-то типа могут ли люди с полностью одинаковыми ФИО сесть в одно такси. Редко встречается и когда произойдёт, они просто сядут в разные, никаких проблем. Но осадок остаётся, логики не видно.

L>Это не "часть типа" (record syntax — всего лишь сахар), а полноценная функция. В Haskell строгая типизация, для ad-hoc полиморфизма есть классы типов. Моя аналогия с классами подчёркивает именно этот факт. Что ты пытаешься сказать своей?


Именно эта "полноценность", то есть, что эта функция находится в глобальном namespace и есть проблема.

Имеется тип
data Maybe a = Just a | Nothing
. Значение Just 3, лично я, рассматриваю, как две части, несущие отдельную информацию. Just (как и Nothing) несёт информацию о том, что это значение типа Maybe. 3 это вторая часть значения, несёт отдельную информацию.

Теперь рассмотрим гипотетический тип
data FIO = FIO { first :: String, last :: String }
. Значение FIO "vasya" "pupkin" я рассматриваю, как состоящее из трёх частей. И функция first — это getter второй части ("vasya"). Если вы согласны с такими терминами, объясните, какой смысл в глобальном getter-е, который работает с одним конкретным типом. Вот если б можно было делать оверлоадинг по типам, т.е. first FIO и first BIO это разные функции, тогда проблемы нет.

Где тут не подходит аналогия с классами. В том, что классы типов вроде как глобальная вещь. Если что-то сравниваемо, то оно Eq. Если из чего-то можно взять часть с именем first, то оно может быть FIO, может быть тысячей других типов.

Возможно, вы имели в виду другое. Вы пишете свой класс JSON, я пишу свой, у нас разное видение методов, которые он должен содержать. Действительно, не получится нам жить в одном неймспейсе, увышка. Но по-моему, разница с record syntax здесь очевидна. То есть глобальность классов гораздо более логична, чем глобальность getter-ов record-ов.
Re[17]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 12.01.10 15:56
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Теперь рассмотрим гипотетический тип
data FIO = FIO { first :: String, last :: String }
. Значение FIO "vasya" "pupkin" я рассматриваю, как состоящее из трёх частей. И функция first — это getter второй части ("vasya"). Если вы согласны с такими терминами, объясните, какой смысл в глобальном getter-е, который работает с одним конкретным типом. Вот если б можно было делать оверлоадинг по типам, т.е. first FIO и first BIO это разные функции, тогда проблемы нет.


Потому что я не рассматриваю его как getter, который не существует без некоего объекта. Потому что для меня это полноценный гражданин, а не его пальто. Вряд ли мне потребуется функция, которая работает одинаково и с FIO и с BIO, т.к. first у них ну очень разные операции, и использовать я их буду в разных местах. Именно поэтому и называться у меня они будут по-разному.

T>Где тут не подходит аналогия с классами. В том, что классы типов вроде как глобальная вещь. Если что-то сравниваемо, то оно Eq. Если из чего-то можно взять часть с именем first, то оно может быть FIO, может быть тысячей других типов.


В случае (==) семантика одна для разных типов, это сравнение, что такое first для FIO, и что он для Pair/List/BIO? Это разные вещи. И называться они должны по разному. Что такое map first?

T>Возможно, вы имели в виду другое. Вы пишете свой класс JSON, я пишу свой, у нас разное видение методов, которые он должен содержать. Действительно, не получится нам жить в одном неймспейсе, увышка. Но по-моему, разница с record syntax здесь очевидна. То есть глобальность классов гораздо более логична, чем глобальность getter-ов record-ов.


У записей нет геттеров, забудь ОО.
У классов нет методов, забудь ОО.

Глобальность классов самих по себе не важна. Важна глобальность перегружаемых функций. Аналогично глобальности меток для полей записи (а не геттеров).

Ещё аналогия

class Named n where
    type E n
    first :: n -> E n
    last :: n -> E n

class CanSetFirst c where
    type F c 
    first :: c -> F c -> c


Две функции с одним именем и разной семантикой. В разных классах. Что изменится, если мы поместим их в разные записи?

Подчеркну, я понимаю твою точку зрения. Но она идёт от ОО. А на Haskell не нужен ОО-дизайн.

На этот счёт было много разговоров (сходу вспоминается extensible records), но пока в Haskell ничего не изменили. По мне, так если каким либо образом метки вынесут из неймспейса функций, то это будет очень неверный шаг.
Re[18]: Inline records
От: Temoto  
Дата: 12.01.10 16:27
Оценка:
T>>Где тут не подходит аналогия с классами. В том, что классы типов вроде как глобальная вещь. Если что-то сравниваемо, то оно Eq. Если из чего-то можно взять часть с именем first, то оно может быть FIO, может быть тысячей других типов.

L>В случае (==) семантика одна для разных типов, это сравнение, что такое first для FIO, и что он для Pair/List/BIO? Это разные вещи. И называться они должны по разному. Что такое map first?


map first непонятно что. Почему? Потому что first в глобальном пространстве имён. map FIO.first (любые вариации типа map FIO $ first, map first $> FIO, неважно) это понятно что такое.

T>>Возможно, вы имели в виду другое. Вы пишете свой класс JSON, я пишу свой, у нас разное видение методов, которые он должен содержать. Действительно, не получится нам жить в одном неймспейсе, увышка. Но по-моему, разница с record syntax здесь очевидна. То есть глобальность классов гораздо более логична, чем глобальность getter-ов record-ов.


L>У записей нет геттеров, забудь ОО.


Это не ОО, это что-то из common sense, просто я использовал термин из ОО. Геттер это функция, которая читает часть сложного значения. Вся разница с ООП только в синтаксисе. Там x.first, а тут first x. Просто в ООП геттер ещё и в другом неймспейсе.

L>У классов нет методов, забудь ОО.


Это опять не из ОО, это официальная хацкельская терминология.

L>Глобальность классов самих по себе не важна. Важна глобальность перегружаемых функций. Аналогично глобальности меток для полей записи (а не геттеров).


L>Ещё аналогия

[...]
L>Две функции с одним именем и разной семантикой. В разных классах. Что изменится, если мы поместим их в разные записи?

Да-да! Вот если б точно такая же перегрузка была бы и для записей — всё было бы шоколадно. Подозреваю, что для этого record syntax должен прозрачно генерить класс на каждый тип записи, и, наверное, это дорого.

L>Подчеркну, я понимаю твою точку зрения. Но она идёт от ОО. А на Haskell не нужен ОО-дизайн.


Может быть я себя обманываю, но мне кажется, что она не идёт от ОО. Я не хочу Java классов, методов объектов, public/private и пр.

L>На этот счёт было много разговоров (сходу вспоминается extensible records), но пока в Haskell ничего не изменили. По мне, так если каким либо образом метки вынесут из неймспейса функций, то это будет очень неверный шаг.


Ясно, спасибо.
Re[19]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 13.01.10 08:29
Оценка:
Здравствуйте, Temoto, Вы писали:

T>map first непонятно что. Почему? Потому что first в глобальном пространстве имён. map FIO.first (любые вариации типа map FIO $ first, map first $> FIO, неважно) это понятно что такое.


А, понял теперь о чём ты. Такого предложения я не слышал. Придётся всегда рядом с first писать FIO? Чем это отличается от fioFirst?

T>Это опять не из ОО, это официальная хацкельская терминология.


Да, сорри.

T>Да-да! Вот если б точно такая же перегрузка была бы и для записей — всё было бы шоколадно. Подозреваю, что для этого record syntax должен прозрачно генерить класс на каждый тип записи, и, наверное, это дорого.


Такое предложение было. Можно реализовать на TH.

T>Может быть я себя обманываю, но мне кажется, что она не идёт от ОО. Я не хочу Java классов, методов объектов, public/private и пр.


Это хорошо.
Re[19]: Inline records
От: Mr.Cat  
Дата: 13.01.10 08:42
Оценка:
Здравствуйте, Temoto, Вы писали:
T>Это не ОО, это что-то из common sense, просто я использовал термин из ОО. Геттер это функция, которая читает часть сложного значения. Вся разница с ООП только в синтаксисе. Там x.first, а тут first x. Просто в ООП геттер ещё и в другом неймспейсе.
Какие еще записи? АТД! И разбирать их паттерн-матчингом, никаких геттеров.
Re[20]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 13.01.10 09:39
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>Какие еще записи? АТД! И разбирать их паттерн-матчингом, никаких геттеров.


Проблема в том, что некоторые АТД содержат много полей.
Re[20]: Inline records
От: Temoto  
Дата: 13.01.10 12:25
Оценка:
T>>map first непонятно что. Почему? Потому что first в глобальном пространстве имён. map FIO.first (любые вариации типа map FIO $ first, map first $> FIO, неважно) это понятно что такое.

L>А, понял теперь о чём ты. Такого предложения я не слышал. Придётся всегда рядом с first писать FIO? Чем это отличается от fioFirst?


Повторением "fio" в определении типа.

data LongType = Constructor { longtypeFirst::String, longtypeBob::Int }
Re[21]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 14.01.10 09:39
Оценка:
Здравствуйте, Temoto, Вы писали:

L>>А, понял теперь о чём ты. Такого предложения я не слышал. Придётся всегда рядом с first писать FIO? Чем это отличается от fioFirst?


T>Повторением "fio" в определении типа.


T>
T>data LongType = Constructor { longtypeFirst::String, longtypeBob::Int }
T>


Это не проблема, префиксы делают маленькими — ltFirst, ltBob. Разницы же в использовании не будет (LongType.First vs longTypeFirst).

Как раз сейчас в haskell-cafe очередное обсуждение этой темы

http://www.mail-archive.com/haskell-cafe@haskell.org/msg69969.html

Рекомендую не заморачиваться. Реального выигрыша не будет, т.к. это даже не ad-hoc полиморфизм — не будет общих функций, работающих над обоими first. Код не уменьшится.
Re[21]: Inline records
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 14.01.10 09:43
Оценка:
Здравствуйте, Temoto, Вы писали:

Вот один из пропозалов TypeDirectedNameResolution.
Я помню, наткнувшись на него, поморщился и забыл.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.