Использование tuple
От: IDL  
Дата: 05.10.09 19:17
Оценка:
В FW 4 появился новый тип tuple, перенятый с функциональных языков.
В общем понятно предназначение его — получить возможность вернуть более одного параметра из функции.
Но насколько это делает код более понятным, ведь значения возвращаемые из tuple без именные.
Что можно понять глядя на функцию возвращающую tuple?
Re: Использование tuple
От: nikov США http://www.linkedin.com/in/nikov
Дата: 05.10.09 19:23
Оценка: +7
Здравствуйте, IDL, Вы писали:

IDL>Что можно понять глядя на функцию возвращающую tuple?


Какой нибудь приватный метод
def GetNameAndLength() : string * int


написать гораздо проще и удобнее, чем создавать отдельный класс с двумя именованными свойствами. Но в языках без встроенного pattern matching'а кортежи не очень удобно использовать.
Re[2]: Использование tuple
От: IDL  
Дата: 05.10.09 20:12
Оценка:
Здравствуйте, nikov, Вы писали:

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


IDL>>Что можно понять глядя на функцию возвращающую tuple?


N>Какой нибудь приватный метод

N>
N>def GetNameAndLength() : string * int
N>


N>написать гораздо проще и удобнее, чем создавать отдельный класс с двумя именованными свойствами. Но в языках без встроенного pattern matching'а кортежи не очень удобно использовать.


Но всё равно, смысл возвращаемого значения не совсем понятно.
Re: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 05.10.09 20:20
Оценка: +3
Здравствуйте, IDL, Вы писали:

IDL>В FW 4 появился новый тип tuple, перенятый с функциональных языков.


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

IDL>Что можно понять глядя на функцию возвращающую tuple?


Для нормального использования кортежей нужен язык с соответствующим синтаксисом.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237 on Windows 7 6.1.7100.0>>
AVK Blog
Re[3]: Использование tuple
От: IT Россия linq2db.com
Дата: 06.10.09 00:36
Оценка:
Здравствуйте, IDL, Вы писали:

N>>Какой нибудь приватный метод

N>>
N>>def GetName() : string
N>>


IDL>Но всё равно, смысл возвращаемого значения не совсем понятно.


А так понятней стало?
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: Использование tuple
От: IDL  
Дата: 06.10.09 04:14
Оценка:
Здравствуйте, IT, Вы писали:

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


N>>>Какой нибудь приватный метод

N>>>
N>>>def GetName() : string
N>>>


IDL>>Но всё равно, смысл возвращаемого значения не совсем понятно.


IT>А так понятней стало?


Из названия функции понятно, что функция возвращает имя, здесь нет неопределённости, а если это кортеж который возвращает имя и фамилию, то не очень понятно где имя, а где фамилия.
Я не возражаю против кортежей, я и сам их использую, очень удобно, но насколько понятно другим?
Просто пытаюсь выработать определённое правило по их использованию. Где проходит красная линия, которая из-за удобства вредит пониманию.
Re[5]: Использование tuple
От: IT Россия linq2db.com
Дата: 06.10.09 05:11
Оценка:
Здравствуйте, IDL, Вы писали:

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


Назови свою функцию GetFirstAndLastNames, будет тоже самое.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Использование tuple
От: IDL  
Дата: 06.10.09 05:47
Оценка:
IDL>>Что можно понять глядя на функцию возвращающую tuple?

AVK>Для нормального использования кортежей нужен язык с соответствующим синтаксисом.

Не совсем понятно о какой поддержке идёт речь
Re[6]: Использование tuple
От: IDL  
Дата: 06.10.09 05:49
Оценка:
Здравствуйте, IT, Вы писали:

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


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


IT>Назови свою функцию GetFirstAndLastNames, будет тоже самое.

Что-то мне не очень нравиться привязывать имена функции к количеству и порядку елементов в кортеже.
Re[7]: Использование tuple
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.10.09 06:41
Оценка:
Здравствуйте, IDL, Вы писали:

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


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


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


IT>>Назови свою функцию GetFirstAndLastNames, будет тоже самое.

IDL>Что-то мне не очень нравиться привязывать имена функции к количеству и порядку елементов в кортеже.

Это от того что язык слабоват.
На нормальном языке можно написать так:

let (firstName, lastName) = GetFirstAndLastNames()


И никаких проблем с читаемостью не будет.
Re[8]: Использование tuple
От: Undying Россия  
Дата: 06.10.09 06:57
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Это от того что язык слабоват.

G>На нормальном языке можно написать так:

G>
G>let (firstName, lastName) = GetFirstAndLastNames()
G>


G>И никаких проблем с читаемостью не будет.


Это все равно костыль. При написании этой строчки нужно знать, что функция возвращает именно firstName и lastName и именно в таком порядке. При использовании честных объектов этого знать не нужно.
Re: Использование tuple
От: Undying Россия  
Дата: 06.10.09 07:01
Оценка: 1 (1)
Здравствуйте, IDL, Вы писали:

IDL>В FW 4 появился новый тип tuple, перенятый с функциональных языков.

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

На мой взгляд, tuple можно использовать если объект нужного вида встречается (будет встречаться) в проекте 1-2 раза. Если чаще, то нужно писать объект. Из этого в частности следует, что tuple недопустим для использования в публичном интерфейсе класса.
Re[9]: Использование tuple
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.10.09 07:10
Оценка:
Здравствуйте, Undying, Вы писали:

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


G>>Это от того что язык слабоват.

G>>На нормальном языке можно написать так:

G>>
G>>let (firstName, lastName) = GetFirstAndLastNames()
G>>


G>>И никаких проблем с читаемостью не будет.


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


Пока ты не посмотришь внутрь объекта не узнаешь что там на самом деле возвращается.
Аналогично для кортежей — надо один раз прочитать описание. На читаемость кода такая запись не повлияет (ИМХО даже улучшит)
Re: Использование tuple
От: Рысцов Денис  
Дата: 06.10.09 07:16
Оценка:
Здравствуйте, IDL, Вы писали:

IDL>В FW 4 появился новый тип tuple, перенятый с функциональных языков.

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

С кортежами в реальной жизни познакомился только переведя разработку своей дипломной работы на Nemerle. Это было где-то год назад, поэтому я думаю, что я знаком с применением коржей на долгоживущим проекте. Плюсами кортежей оказалась гибкость при реорганизации кода и простота использования (отсутствие необходимости создавать классы только для того, что бы вернуть составное значение). Но в последствии оказалось, что иногда сложно быстро понять что за кортеж возвращает функция, поэтому я пришел к следующей схеме:
1. Стараться не использовать кортежи в публичных методах.
2. Использовать кортежи только в private методах и локальных функциях.
3. Всегда писать комментарии перед функциями, в которых описывать возвращаемый тип кортежа.
Re[2]: Использование tuple
От: _FRED_ Черногория
Дата: 06.10.09 07:27
Оценка:
Здравствуйте, nikov, Вы писали:

IDL>>Что можно понять глядя на функцию возвращающую tuple?

N>Какой нибудь приватный метод
N>def GetNameAndLength() : string * int

N>написать гораздо проще и удобнее, чем создавать отдельный класс с двумя именованными свойствами. Но в языках без встроенного pattern matching'а кортежи не очень удобно использовать.

Кстати, а как в функциональных языках (в первую очередь, .NET-ных): принято ли использовать таплы в public API или рекомендуется использовать их только в локальных переменных и методах?
Help will always be given at Hogwarts to those who ask for it.
Re[8]: Использование tuple
От: IDL  
Дата: 06.10.09 07:38
Оценка:
Здравствуйте, gandjustas, Вы писали:

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


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


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


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


IT>>>Назови свою функцию GetFirstAndLastNames, будет тоже самое.

IDL>>Что-то мне не очень нравиться привязывать имена функции к количеству и порядку елементов в кортеже.

G>Это от того что язык слабоват.

G>На нормальном языке можно написать так:

G>
G>let (firstName, lastName) = GetFirstAndLastNames()
G>


G>И никаких проблем с читаемостью не будет.

Ну так конечно получше.
В принципе кортеж удобно использовать локально, тогда его создание, использование находиться перед глазами и возникают меньше вопросов по их использованию.
Ето очень хорошо ложится по видимость анонимных объектов.
Re[2]: Использование tuple
От: IDL  
Дата: 06.10.09 07:41
Оценка:
Здравствуйте, Рысцов Денис, Вы писали:

РД>Здравствуйте, IDL, Вы писали:


IDL>>В FW 4 появился новый тип tuple, перенятый с функциональных языков.

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

РД>С кортежами в реальной жизни познакомился только переведя разработку своей дипломной работы на Nemerle. Это было где-то год назад, поэтому я думаю, что я знаком с применением коржей на долгоживущим проекте. Плюсами кортежей оказалась гибкость при реорганизации кода и простота использования (отсутствие необходимости создавать классы только для того, что бы вернуть составное значение). Но в последствии оказалось, что иногда сложно быстро понять что за кортеж возвращает функция, поэтому я пришел к следующей схеме:

РД>1. Стараться не использовать кортежи в публичных методах.
РД>2. Использовать кортежи только в private методах и локальных функциях.
РД>3. Всегда писать комментарии перед функциями, в которых описывать возвращаемый тип кортежа.

Я тоже столкнулся именно с такими проблемами и пришёл к таким выводам.
С одной стороны не надо на каждый чих создавать объект, но сдругой не сразу и поймёшь, что вернул.
Re[10]: Использование tuple
От: Undying Россия  
Дата: 06.10.09 08:07
Оценка: 3 (1)
Здравствуйте, gandjustas, Вы писали:

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


G>Пока ты не посмотришь внутрь объекта не узнаешь что там на самом деле возвращается.


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

G>Аналогично для кортежей — надо один раз прочитать описание. На читаемость кода такая запись не повлияет (ИМХО даже улучшит)


Нормальной записью была бы такая:

Tuple<string FirstName, string LastName> GetFirstAndLastNames()
{
  return new Tuple(firstName, lastName);
}


Эта запись ничем использованию честных объектов не уступает, здесь при декларации функции четко прописано, что функция возвращает. А let это костыль, хотя возможно и полезный в некоторых случаях.
Re[11]: Использование tuple
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.10.09 08:14
Оценка:
Здравствуйте, Undying, Вы писали:

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


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


G>>Пока ты не посмотришь внутрь объекта не узнаешь что там на самом деле возвращается.


U>При использовании честных объектов интеллисенса, чтобы понять, обычно достаточно. В случае же Tuple интеллисенс практически ничем помочь не может.

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

G>>Аналогично для кортежей — надо один раз прочитать описание. На читаемость кода такая запись не повлияет (ИМХО даже улучшит)


U>Нормальной записью была бы такая:


U>
U>Tuple<string FirstName, string LastName> GetFirstAndLastNames()
U>{
U>  return new Tuple(firstName, lastName);
U>}
U>


U>Эта запись ничем использованию честных объектов не уступает, здесь при декларации функции четко прописано, что функция возвращает. А let это костыль, хотя возможно и полезный в некоторых случаях.


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

Но тут возникают проблемы
1)Насколько записи и одинаковыми типами и разными именами полей соместимы нежду собой?
2)То же что выше, но когда типы в разных сборках?
3)Насколько такие типы будут совмсетимы с обычными туплами?

ЗЫ. Как уже отмечали выше тупы применять в публичном контракте не стоит без крайней необходимости.
Re[12]: Использование tuple
От: Undying Россия  
Дата: 06.10.09 08:31
Оценка:
Здравствуйте, gandjustas, Вы писали:

U>>Нормальной записью была бы такая:


U>>
U>>Tuple<string FirstName, string LastName> GetFirstAndLastNames()
U>>{
U>>  return new Tuple(firstName, lastName);
U>>}
U>>


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


G>Но тут возникают проблемы

G>1)Насколько записи и одинаковыми типами и разными именами полей соместимы нежду собой?
G>2)То же что выше, но когда типы в разных сборках?
G>3)Насколько такие типы будут совмсетимы с обычными туплами?

Tuple<string FirstName, string LastName> в любом месте и в любой сборке должен являться одним и тем же типом. С неименнованными туплами не должен иметь ничего общего.

G>ЗЫ. Как уже отмечали выше тупы применять в публичном контракте не стоит без крайней необходимости.


Сегодняшние туплы, конечно, нельзя ни в коем случае. Именованные туплы было бы можно.
Re[13]: Использование tuple
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.10.09 08:37
Оценка:
Здравствуйте, Undying, Вы писали:

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


U>>>Нормальной записью была бы такая:


U>>>
U>>>Tuple<string FirstName, string LastName> GetFirstAndLastNames()
U>>>{
U>>>  return new Tuple(firstName, lastName);
U>>>}
U>>>


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


G>>Но тут возникают проблемы

G>>1)Насколько записи и одинаковыми типами и разными именами полей соместимы нежду собой?
G>>2)То же что выше, но когда типы в разных сборках?
G>>3)Насколько такие типы будут совмсетимы с обычными туплами?

U>Tuple<string FirstName, string LastName> в любом месте и в любой сборке должен являться одним и тем же типом. С неименнованными туплами не должен иметь ничего общего.

И как это будет работать в существующей системе типов C# и CLR?

G>>ЗЫ. Как уже отмечали выше тупы применять в публичном контракте не стоит без крайней необходимости.

U>Сегодняшние туплы, конечно, нельзя ни в коем случае. Именованные туплы было бы можно.
То что ты описываешь — есть анонимные объекты в C#. Есть объекивные причины по которым их нельзя возвращать из методов класса.
Re[3]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 06.10.09 08:59
Оценка:
Здравствуйте, IDL, Вы писали:

IDL>Не совсем понятно о какой поддержке идёт речь


Ну посмотри на F#, что я тут документацию пересказывать буду.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237 on Windows 7 6.1.7100.0>>
AVK Blog
Re[9]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 06.10.09 08:59
Оценка:
Здравствуйте, Undying, Вы писали:

U>Это все равно костыль. При написании этой строчки нужно знать, что функция возвращает именно firstName и lastName и именно в таком порядке.


Ситуация, абсолютно идентичная параметрам метода. Как любит говорить Эрик Мейерс — налицо дуализм, и это очень хорошо. Кортеж — дуальный комплимент параметров метода, более качественная замена уродливым out параметрам. Вот для явного отражения сего факта и нужен совпадающий синтаксис. В шарпе и VB, увы, позиционное использование кортежа невозможно, а явное обращение к First, Second (или что там в tuple) превращает код в малочитаемое гамно.

U> При использовании честных объектов этого знать не нужно.


Никто не мешает тебе использовать объекты. Кортежи на уровень ниже, и то, что есть такой класс Tuple это деталь реализации.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237 on Windows 7 6.1.7100.0>>
AVK Blog
Re[12]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 06.10.09 08:59
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Ну это уже не туплы, а записи (records). Сейчас анонимные типы выполняют аналогичную функциональность


Функциональность у анонимных типов все же не совсем аналогичная. Скорее они решают сходные задачи.

G>Но тут возникают проблемы

G>1)Насколько записи и одинаковыми типами и разными именами полей соместимы нежду собой?

Очевидно, совсем несовместимы

G>2)То же что выше, но когда типы в разных сборках?


Должны быть совместимы, но требуется модификация JIT

G>3)Насколько такие типы будут совмсетимы с обычными туплами?


Очевидно, совсем несовместимы.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237 on Windows 7 6.1.7100.0>>
AVK Blog
Re[14]: Использование tuple
От: Undying Россия  
Дата: 06.10.09 09:24
Оценка:
Здравствуйте, gandjustas, Вы писали:

U>>Tuple<string FirstName, string LastName> в любом месте и в любой сборке должен являться одним и тем же типом. С неименнованными туплами не должен иметь ничего общего.

G>И как это будет работать в существующей системе типов C# и CLR?

А при чем здесь существующая система типов C#? Речь о том каким должно быть хорошее решение. Если ограничения C# и CLR не позволяют хорошее решение реализовать даже в будущих версиях, то это плохо, но это совсем другой вопрос.
Re[10]: Использование tuple
От: Undying Россия  
Дата: 06.10.09 09:40
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Ситуация, абсолютно идентичная параметрам метода. Как любит говорить Эрик Мейерс — налицо дуализм, и это очень хорошо. Кортеж — дуальный комплимент параметров метода, более качественная замена уродливым out параметрам. Вот для явного отражения сего факта и нужен совпадающий синтаксис. В шарпе и VB, увы, позиционное использование кортежа невозможно, а явное обращение к First, Second (или что там в tuple) превращает код в малочитаемое гамно.


Не понял почему кортеж нужно ограниченно понимать только как замену out параметрам?

Чем:

interface ITest
{
  Tuple<string FirstName, string LastName> Name { get; }
}


хуже, чем


class Name
{
  string FirstName;
  string LastName;
}

inteface ITest
{
  Name Name { get; }
}


Первый случай как бы не более понятен (как, к примеру, Func<T, T, int> на практике оказывается понятнее Comparison<T>) и при этом кода требует в разы меньше.

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


Вообще бы я вопрос шире поставил. Мне бы хотелось не только именованные туплы видеть, но и возможность задания шаблона не только для типа, но и для названий свойств/методов. Т.е. чтобы можно было писать не только KeyValuePair<int, string>, но и KeyValuePair<int, string, Key as Id, Value as Text>.
Re[7]: Использование tuple
От: IT Россия linq2db.com
Дата: 06.10.09 13:08
Оценка:
Здравствуйте, IDL, Вы писали:

IDL>Что-то мне не очень нравиться привязывать имена функции к количеству и порядку елементов в кортеже.


А к возвращаемому значению нравится?

В общем, на мой взгляд эта проблема надумана. Может, конечно, определённые неудобства и есть, но по сравнению с полным отсутствием кортежей эти неудобства не видны в микроскоп.
Если нам не помогут, то мы тоже никого не пощадим.
Re[11]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 06.10.09 15:37
Оценка:
Здравствуйте, Undying, Вы писали:

U>Не понял почему кортеж нужно ограниченно понимать только как замену out параметрам?


Почему ограниченно? Это очень широкое применение.

U>Чем:


U>
U>interface ITest
U>{
U>  Tuple<string FirstName, string LastName> Name { get; }
U>}
U>


U>хуже, чем


U>
U>class Name
U>{
U>  string FirstName;
U>  string LastName;
U>}

U>inteface ITest
U>{
U>  Name Name { get; }
U>}
U>


U>Первый случай как бы не более понятен


По какой причине? Мне так второй более понятен, особенно при использовании.

U> (как, к примеру, Func<T, T, int> на практике оказывается понятнее Comparison<T>) и при этом кода требует в разы меньше.


Негодная аналогия. Кроме аналогии что то еще есть?

U>Вообще бы я вопрос шире поставил. Мне бы хотелось не только именованные туплы видеть, но и возможность задания шаблона не только для типа, но и для названий свойств/методов.


Это называется утиная типизация и ее много раз здесь уже обсуждали.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237 on Windows 7 6.1.7100.0>>
AVK Blog
Re: Использование tuple
От: dotneter  
Дата: 06.10.09 19:02
Оценка:
Имхо
Использовать стоит только в случае набора классов.
(new SomeClass1(), new SomeClass2())
иначе записи,
new {Value = 1, Text = "Something"}
по крайтей мере когда мейнстрим преодолеет каменный век и они таки получат распространение.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[12]: Использование tuple
От: Undying Россия  
Дата: 07.10.09 03:43
Оценка:
Здравствуйте, AndrewVK, Вы писали:

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


U>>Не понял почему кортеж нужно ограниченно понимать только как замену out параметрам?

AVK>Почему ограниченно? Это очень широкое применение.

Потребность в out'ных параметрах у меня возникает по очень большим праздникам. Кроме того я плохо понимаю, чем добавление неименованных tuple в язык принципиально улучшит ситуацию в сравнение с сегодняшних tuple, которые за пять минут пишутся на дженериках.

U>>Чем:


U>>
U>>interface ITest
U>>{
U>>  Tuple<string FirstName, string LastName> Name { get; }
U>>}
U>>


U>>хуже, чем


U>>
U>>inteface ITest
U>>{
U>>  Name Name { get; }
U>>}
U>>


U>>Первый случай как бы не более понятен


AVK>По какой причине? Мне так второй более понятен, особенно при использовании.


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

U>>Вообще бы я вопрос шире поставил. Мне бы хотелось не только именованные туплы видеть, но и возможность задания шаблона не только для типа, но и для названий свойств/методов.


AVK>Это называется утиная типизация и ее много раз здесь уже обсуждали.


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


Причем здесь это? Запись KeyValuePair<int, string, Key as Id, Value as Text> эквивалентна копипасту KeyValuePair с созданием класса IdTextPair со свойствами Id и Text, никакой совместимости между KeyValuePair и IdTextPair быть не должно. Такая фича нужна для того, чтобы за пять копеек получать классы с понятными названиями методов, сохраняющими все вкусности логики исходного класса (например, в простейшем случае это перекрытый Equals и GetHashCode). При этом от громоздкой записи легко избавиться либо через наследование, либо через полноценный псевдоним (т.е. using область видимости которого не текущий файл, а текущий namespace). Второе предпочтительней. Т.е. либо так:

class IdTextPair : KeyValuePair<int, string, Key as Id, Value as Text>
{
}


либо так:

namespace Test
{
   using IdTextPair = KeyValuePair<int, string, Key as Id, Value as Text>;
}
Re[13]: Использование tuple
От: samius Япония http://sams-tricks.blogspot.com
Дата: 07.10.09 06:03
Оценка: +1
Здравствуйте, Undying, Вы писали:

U>Потребность в out'ных параметрах у меня возникает по очень большим праздникам. Кроме того я плохо понимаю, чем добавление неименованных tuple в язык принципиально улучшит ситуацию в сравнение с сегодняшних tuple, которые за пять минут пишутся на дженериках.


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

U>>>Чем:


U>>>
U>>>interface ITest
U>>>{
U>>>  Tuple<string FirstName, string LastName> Name { get; }
U>>>}
U>>>


U>В первом случае я могу быть уверен, что Name это простое хранилище FirstName и LastName, не имеющее неявной логики. Во втором случае объект типа Name может иметь любую логику, в том числе и весьма неочевидную для меня как пользователя этого объекта.


Гибрид тупла с анонимным типом — вещь интересная, но это большой и не очевидный шаг для языка. И если фантазировать на эту тему, то чем пихать имена полей в дженерик параметры, я бы предпочел что-то в духе анонимного типа
{FirstName :string, LastName : string} Name { get; }

Но это уже вариации не на тему туплов, а на тему вытаскивания анонимных типов из метода.
Re[14]: Использование tuple
От: Undying Россия  
Дата: 07.10.09 06:53
Оценка:
Здравствуйте, samius, Вы писали:

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


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

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

S>
S>{FirstName :string, LastName : string} Name { get; }
S>

S>Но это уже вариации не на тему туплов, а на тему вытаскивания анонимных типов из метода.

Я пока что обсуждаю то, что именованные кортежи в языке были бы полезны. Способ их декларации это уже другой вопрос, особых возражений против твоего способа записи у меня нет.
Re[15]: Использование tuple
От: samius Япония http://sams-tricks.blogspot.com
Дата: 07.10.09 08:50
Оценка:
Здравствуйте, Undying, Вы писали:

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


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


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


Да, стандартные туплы могли бы быть еще в C# 2.0. Обошлись бы без самокатов.
Re[13]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 07.10.09 11:24
Оценка:
Здравствуйте, Undying, Вы писали:

U>Потребность в out'ных параметрах у меня возникает по очень большим праздникам.


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

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


Ну, тут тебе просто надо посмотреть, как оно выглядит в поддерживающих их языках.
... << RSDN@Home 1.2.0 alpha 4 rev. 1245 on Windows 7 6.1.7100.0>>
AVK Blog
Re[11]: Использование tuple
От: mrTwister Россия  
Дата: 07.10.09 11:56
Оценка:
Здравствуйте, Undying, Вы писали:


U>Чем:


U>
U>interface ITest
U>{
U>  Tuple<string FirstName, string LastName> Name { get; }
U>}
U>


U>хуже, чем



U>
U>class Name
U>{
U>  string FirstName;
U>  string LastName;
U>}

U>inteface ITest
U>{
U>  Name Name { get; }
U>}
U>


Тем, что при необходимости в класс Name можно добавить методы, свойства и пр.
лэт ми спик фром май харт
Re[14]: Использование tuple
От: Undying Россия  
Дата: 08.10.09 04:17
Оценка:
Здравствуйте, AndrewVK, Вы писали:

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


U>>Потребность в out'ных параметрах у меня возникает по очень большим праздникам.


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

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

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


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


В Питоне, например, так:

def func(x,y):
    # code to compute a and b
    return (a,b)

(a,b) = func(1,2)


В сравнении с вариантом из сегодняшнего шарпа:

Tuple<double, double> func(double x, double y)
{
  return _.Tuple(x, y);
}

var result = func(1,2);


вариант Питона лучше только одним, возможностью лаконично, но явно скастить возвращаемый тупл к туплу с произвольным названием полей. Это, конечно, лучше чем ничего, но один из главных принципов программирования — "код должен быть самодокументируемым" по-прежнему нарушается. В Питоне точно также как в шарпе при использовании функции возвращающей тупл нужно магическим образом догадываться, что именно хранит конкретное поле тупла. Именованные туплы позволили бы писать самодокументированный код, сохраняя все преимущества неименнованных туплов.
Re[14]: Использование tuple
От: FR  
Дата: 08.10.09 05:26
Оценка: +1
Здравствуйте, AndrewVK, Вы писали:

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


Не часто, так как в таких языках (ml, haskell) обычно поддерживается карринг. Кортежи наоборот мешают каррингу, поэтому использование их в качестве аргументов (кроме конечно случаев когда нужен именно кортеж) считается плохим стилем.
Re[15]: Использование tuple
От: FR  
Дата: 08.10.09 05:35
Оценка:
Здравствуйте, Undying, Вы писали:

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


А так?
def new_point(x, y):
    # code to compute a and b
    return (a,b)



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


Именованные туплы это по сути обычные сишные структуры, они и так есть.
Re[16]: Использование tuple
От: Undying Россия  
Дата: 08.10.09 06:08
Оценка:
Здравствуйте, FR, Вы писали:

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


FR>А так?

FR>
FR>def new_point(x, y):
FR>    # code to compute a and b
FR>    return (a,b)
FR>


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

(firstName, lastName) = GetFirstNameAndLastName();


Не шибко красиво честно скажем.

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


FR>Именованные туплы это по сути обычные сишные структуры, они и так есть.


Чтобы написать обычную сишную структуру, да еще и желательно с перекрытием Equals и GetHashCode, чтобы не получить в будущем граблей, нужно написать кучу кода. Именованные туплы позволили бы делать тоже самое половиной строчки кода.
Re[17]: Использование tuple
От: FR  
Дата: 08.10.09 06:47
Оценка:
Здравствуйте, Undying, Вы писали:


U>Т.е. функцию возращающее имя человека в виде тупла ты предлагаешь назвать GetFirstNameAndLastName? В принципе при использовании неименнованных туплов это часто лучший вариант, но малость не лаконичный при использовании.


Лаконичный чем твой генерик выше.

U>
U>(firstName, lastName) = GetFirstNameAndLastName();
U>


U>Не шибко красиво честно скажем.


Нормально.


FR>>Именованные туплы это по сути обычные сишные структуры, они и так есть.


U>Чтобы написать обычную сишную структуру, да еще и желательно с перекрытием Equals и GetHashCode, чтобы не получить в будущем граблей, нужно написать кучу кода. Именованные туплы позволили бы делать тоже самое половиной строчки кода.


Ну это проблемы шарпа, в том же F# куча кода не нужна http://msdn.microsoft.com/en-us/library/dd233184(VS.100).aspx
Re[18]: Использование tuple
От: Undying Россия  
Дата: 08.10.09 07:45
Оценка:
Здравствуйте, FR, Вы писали:

U>>Т.е. функцию возращающее имя человека в виде тупла ты предлагаешь назвать GetFirstNameAndLastName? В принципе при использовании неименнованных туплов это часто лучший вариант, но малость не лаконичный при использовании.


U>>
U>>(firstName, lastName) = GetFirstNameAndLastName();
U>>


FR>Лаконичный чем твой генерик выше.


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

var name = GetName();
Console.WriteLine(name.FirstName);


Гораздо красивее, чем даже на питоне, не говоря уж о сегодняшнем шарпе.

FR>Ну это проблемы шарпа, в том же F# куча кода не нужна http://msdn.microsoft.com/en-us/library/dd233184(VS.100).aspx


Т.е. такая декларация класса

type Point = { x : float; y: float; z: float; }


автоматически перекрывает Equals и GetHashCode?

зы
Вообще я бы хотел даже не именованных туплов, а возможности создавать одной строчкой кода новый тип класса на основе существующего с возможностью переименование полей и методов. Тогда бы и потребность в специальной реализации (т.е. такой для которой не достаточно средств шарпа 2.0) туплов отпала и возможности языка увеличились бы кардинально. F# вроде ничего подобного не позволяет.
Re[8]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 08.10.09 10:13
Оценка:
Здравствуйте, IT, Вы писали:
IT>В общем, на мой взгляд эта проблема надумана.

Не надумана
Автор: Andrei N.Sobchuck
Дата: 26.03.07
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[9]: Использование tuple
От: IT Россия linq2db.com
Дата: 08.10.09 13:48
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

IT>>В общем, на мой взгляд эта проблема надумана.

ANS>Не надумана
Автор: Andrei N.Sobchuck
Дата: 26.03.07


Мы об одной и той же проблеме говорим? Мы тут вроде о туплах, а ты мне ссылку на параметры лямбды
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 09.10.09 08:31
Оценка: +1
Здравствуйте, IT, Вы писали:

IT>Мы об одной и той же проблеме говорим? Мы тут вроде о туплах, а ты мне ссылку на параметры лямбды


Суть одна и та же — поля не именованы. Если типы совпадают — туши свет.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[11]: Использование tuple
От: thesz Россия http://thesz.livejournal.com
Дата: 09.10.09 09:06
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

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


IT>>Мы об одной и той же проблеме говорим? Мы тут вроде о туплах, а ты мне ссылку на параметры лямбды


ANS>Суть одна и та же — поля не именованы. Если типы совпадают — туши свет.


Это не так.

Тип [(String,String)], который получился преобразованием Map.toList ourNameMap, проходит через обработку без всяких структурных изменений внутри пар. Поэтому это не "тушите свет", это, обычно, "следи чуть внимательней".
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[12]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 09.10.09 09:24
Оценка:
Здравствуйте, thesz, Вы писали:

T>Тип [(String,String)], который получился преобразованием Map.toList ourNameMap, проходит через обработку без всяких структурных изменений внутри пар.


К чему тут Map.toList? И что вообще должно получиться в результате Map.toList (хоть к теме топика на прямую это и неотносится)?

T>Поэтому это не "тушите свет", это, обычно, "следи чуть внимательней".


Жаль, что нужно очень внимательно следить, чтобы не перейти грань между "тушите свет" и "следи чуть внимательней".
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[13]: Использование tuple
От: thesz Россия http://thesz.livejournal.com
Дата: 09.10.09 09:54
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

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


T>>Тип [(String,String)], который получился преобразованием Map.toList ourNameMap, проходит через обработку без всяких структурных изменений внутри пар.


ANS>К чему тут Map.toList?


Удобная штука.

ANS>И что вообще должно получиться в результате Map.toList (хоть к теме топика на прямую это и неотносится)?


Список пар "ключ, значение".

T>>Поэтому это не "тушите свет", это, обычно, "следи чуть внимательней".

ANS>Жаль, что нужно очень внимательно следить, чтобы не перейти грань между "тушите свет" и "следи чуть внимательней".

Нет, не надо.

На 600 определений, у которых нет типа и для которых ghc показал типы с помощью -fwarn-missing-signatures в моей программе приходится 38 определений с кортежами, где что-то можно перепутать — (Double,Double,Double), ((Index,Bool),(Index,Index,Side)) и подобные. В паре (String,Int) и тройке (Bool,String,Int) ничего перепутать нельзя, поэтому я такие не учитывал.

Что составляет 6.3%.

Такие определения группируются, буквально, в двух файлах из 79. В остальных случаях это внутренние функции, на которые заводить отдельный тип смысла не имеет (ну, например, функция возвращает (Entry, TreeView, ListStore Param, TreeView, ListStore Function) и используется один раз — зачем для неё тип?).

Итого, менее 6% всех функций и менее 3% файлов требуют чуть более внимательного отношения.

И это в Хаскеле, где пары очень дёшевы.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[14]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 09.10.09 10:12
Оценка:
Здравствуйте, thesz, Вы писали:

ANS>>К чему тут Map.toList?

T>Удобная штука.

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

T>Итого, менее 6% всех функций и менее 3% файлов требуют чуть более внимательного отношения.


10% кода могут породить 90% багов И вообще, странно слышать аргумент про "требуют чуть более внимательного отношения" от человека предпочитающего статическую типизацию перед динамической из-за возможности получить ошибку приведения типов во время выполнения.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 10:13
Оценка:
Здравствуйте, IDL, Вы писали:

IDL>В FW 4 появился новый тип tuple, перенятый с функциональных языков.

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

А что можно понять, глядя на вызов такой функции?

res = fun( x, y, z );

?

А если ее сигнатура такая:

Type fun( Type &, Type, Type & )

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

( res, x, z ) = fun( y );

Неужели не понятнее, что происходит, без сигнатуры функции?
Re[2]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 09.10.09 10:31
Оценка: +1
Здравствуйте, Gaperton, Вы писали:


G>Type fun( Type &, Type, Type & )

G>( res, x, z ) = fun( y );

Разница между 1 и 2 в том, что точек присваивания много, и в каждом будут свои имена, а точка реализации функции одна.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[2]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 10:33
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Неужели не понятнее, что происходит, без сигнатуры функции?


Или у кого-то хватило ума добавить туплы без поддержки паттерн-матчинга, и так как я написал — писать нельзя? И оно выглядит в духе std::pair в С++ — криво, как турецкая сабля? Такой тупл реально бесполезен почти .
Re[15]: Использование tuple
От: thesz Россия http://thesz.livejournal.com
Дата: 09.10.09 10:48
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

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


ANS>>>К чему тут Map.toList?

T>>Удобная штука.
ANS>Если tuples использовать только для пар ключ-значение, то проблем действительно не будет. Только tuples-как-сущность для этого не нужны.

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

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

T>>Итого, менее 6% всех функций и менее 3% файлов требуют чуть более внимательного отношения.


ANS>10% кода могут породить 90% багов


Значит, их надо переписать.

И они будут переписаны, если это действительно поможет избежать ошибок.

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


Для меня важно поменьше напрягаться, а не соблюдать все 613 законов статической типизации.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re[11]: Использование tuple
От: IT Россия linq2db.com
Дата: 09.10.09 13:21
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

IT>>Мы об одной и той же проблеме говорим? Мы тут вроде о туплах, а ты мне ссылку на параметры лямбды


ANS>Суть одна и та же — поля не именованы. Если типы совпадают — туши свет.


В параметрах лябды тоже тушим свет? Да и вообще при вызове любого метода при передаче параметров легко запутаться. Ага.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 14:35
Оценка: 27 (4) +2
Здравствуйте, _FRED_, Вы писали:

_FR>Кстати, а как в функциональных языках (в первую очередь, .NET-ных): принято ли использовать таплы в public API или рекомендуется использовать их только в локальных переменных и методах?


Смотря что за операция. Например, если функция (метод) разделяет список на два других, то совершенно естественно описать ее сигнатуру следующим образом:
public Partition (pred : T -> bool) : list [T] * list [T]

Или вот сигнатура метода отделяющего последний элемент списка от списка:
public DivideLast () : list [T] * T


Короче, все нормально, если разумно. Не разумно возвращать в кортежах поля каких-нибудь бизнес-сущностей. А если из сути функции понятно, что она возвращает, то проблем это не создает.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 14:42
Оценка: 2 (1)
Здравствуйте, IDL, Вы писали:

AVK>>Для нормального использования кортежей нужен язык с соответствующим синтаксисом.

IDL>Не совсем понятно о какой поддержке идёт речь

Как минимум нужны две возможности:
1. Конструирование кортежа, чтобы вместо:
Tuple.Create(1, "abc")

писать просто:
(1, "abc")
2. Декомпозиция кортежа, чтобы вместо:
  def t     = GetNameAdnCount();
  var name  = t.Item2;
  var count = t.Item1;

можно было писать:
  def (count, name) = GetNameAdnCount();

или даже:
  SetNameAdnCount(GetNameAdnCount());


В общем, сахар который позволяет удобно ими манипулировать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 14:45
Оценка: +2
Здравствуйте, IDL, Вы писали:

IDL>Я тоже столкнулся именно с такими проблемами и пришёл к таким выводам.

IDL>С одной стороны не надо на каждый чих создавать объект, но сдругой не сразу и поймёшь, что вернул.

Не обижайтесь, но это говорит о не внятности ваших функций. Например, кому не ясно значение следующего метода list[T]?
    public Partition (pred : T -> bool) : list [T] * list [T]
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 14:47
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Или у кого-то хватило ума добавить туплы без поддержки паттерн-матчинга,


У Майкрософт. Ты тему то прочел?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 14:50
Оценка: +2
Здравствуйте, Gaperton, Вы писали:

G>А что можно понять, глядя на вызов такой функции?


G>res = fun( x, y, z );


G>?


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

Если же функция имеет нормально описанный список параметров, то перейдя к ней или посмотрев ее сигнатуру с помощью интеллисенса можно будет понять все что нужно о ее аргументах.
С кортежам все сложнее. Тут или имя функции должно однозначно говорить о том, что за элементы у этого кортежа, или требуется наличие качественного описания функции. Второй вариант, на мой взгляд уже является плохим. Так что кортежи хороши только там где из значение очевидно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Использование tuple
От: maxkar  
Дата: 09.10.09 15:18
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Например, кому не ясно значение следующего метода list[T]?

VD>
VD>    public Partition (pred : T -> bool) : list [T] * list [T] 
VD>


Мне не ясно, например. Кто первый в этой паре? То, что соответствует предикату или то, что не соответствует? И, самое интересное. Как я должен рассуждать, чтобы получить "правильный" ответ? Документацию я специально не смотрел и на шарпе не пишу.
Re[5]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 19:13
Оценка: +1 -1
Здравствуйте, maxkar, Вы писали:

VD>>Например, кому не ясно значение следующего метода list[T]?

VD>>
VD>>    public Partition (pred : T -> bool) : list [T] * list [T] 
VD>>


M>Мне не ясно, например. Кто первый в этой паре?


Бывает. Наверно в школе с учебниками математики тоже проблемы были?

M> То, что соответствует предикату или то, что не соответствует?


Разумеется сначала идут те что соответствуют, потом те что не соответствуют.

M>И, самое интересное. Как я должен рассуждать, чтобы получить "правильный" ответ? Документацию я специально не смотрел и на шарпе не пишу.


А как ты рассуждаешь когда используешь функцию Filter или Where? А в друг результат будет обратный предикату?

В реальном коде все выглядеть примерно так:
def (fresh, old) = data.Partition(x => x.DateTime.Year >= DateTime.Now.Year);
...
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 21:06
Оценка:
Здравствуйте, VladD2, Вы писали:

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


G>>Или у кого-то хватило ума добавить туплы без поддержки паттерн-матчинга,


VD>У Майкрософт. Ты тему то прочел?


В этот раз нет. Но раз все действительно так — то и говорить не о чем.
Re[3]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 21:09
Оценка: +2
Здравствуйте, VladD2, Вы писали:

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


G>>А что можно понять, глядя на вызов такой функции?


G>>res = fun( x, y, z );


G>>?


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


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

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

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

Разница только в четком отделении output параметров от input. И все. Это — удобно. И это, кстати, главная и по сути единственная штука, которая меня постоянно напрягала при программировании на С++, после знакомства с ФЯ. Остальное можно пережить легко, если уметь обращаться с С++. Но эта мелочь — бесит.
Re[3]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 21:12
Оценка: :)
Здравствуйте, VladD2, Вы писали:

Что, Влад, спорим в этот раз, чтобы поддержать традицию? Повода нет имхо . Но я не против .
Re[3]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 21:22
Оценка: +2
Здравствуйте, Andrei N.Sobchuck, Вы писали:

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



G>>Type fun( Type &, Type, Type & )

G>>( res, x, z ) = fun( y );

ANS>Разница между 1 и 2 в том, что точек присваивания много, и в каждом будут свои имена, а точка реализации функции одна.


Что? Не понял, видать тупой стал. Для начала, не то ты зацитировал для сравнения. Правильно:

G>> res = fun( x, y, z );

G>>( res, x, z ) = fun( y );

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

Ответ. Капитан очевидность сообщает — во втором варианте выходные параметры нельзя спутать с аргументами с побочными эффектами. Почему это важно, и имеет значение? Капитан очевидность ничего не может на это ответить, кроме сказать, что такой код читать существенно проще. А чтобы понять, почему так — надо уже мозг включать, или иметь опыт поддержки.
Re[4]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 21:41
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Интересное соображение. А почему ты считаешь, что интеллисенс не сможет показать тебе типов возвращаемого тупла, которые точно так же являются частью сигнатуры функции, интересно?


Потому что у полей нет имен.

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


С типом проблем нет. Но что за толк от знания, что, скажем, функция CalcSome возвращает тип int * int * int?

G>Разница только в четком отделении output параметров от input. И все. Это — удобно.


А я с этим и не спорю. Кортыжу удобнее out/ref-параметров. Но это другой вопрос не устраняющий наличие проблемы потери информации.

G>И это, кстати, главная и по сути единственная штука, которая меня постоянно напрягала при программировании на С++, после знакомства с ФЯ. Остальное можно пережить легко, если уметь обращаться с С++. Но эта мелочь — бесит.


Везет тебе. Меня больше непрягает то, что за памятю нужно следить, выполнять разные дурацкие ретуальные действия и соблюдать кодекс чести только чтобы случайно не нажить себе проблем на пару вечеров.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 21:45
Оценка: +1
Здравствуйте, Gaperton, Вы писали:

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


G>Что, Влад, спорим в этот раз, чтобы поддержать традицию? Повода нет имхо . Но я не против .


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

Сам использую кортежи постоянно. Но кортежий в 5 элементов стараюсь избегать (чисто интуитивно). Да и по больше части спользую котрежи внутри реализаций, так что бы на ружу они не торчали.

Кстати, очень помогают локальные функции.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 21:53
Оценка:
Здравствуйте, VladD2, Вы писали:

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


G>>Интересное соображение. А почему ты считаешь, что интеллисенс не сможет показать тебе типов возвращаемого тупла, которые точно так же являются частью сигнатуры функции, интересно?


VD>Потому что у полей нет имен.


Зато всегда есть имена принимающих переменных, которые названы так, что все понятно. Этого мало? Кроме того, это имеет вообще значение только в случае, если возвращаемые значения тупла одного типа. Такое бывает редко, и обычно однозначно трактуется, скажем:

Прямоугольник, или линия — ( x1, y1, x2, y2 ).

Диапазон времени — ( startTime, endTime ).

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

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


VD>С типом проблем нет. Но что за толк от знания, что, скажем, функция CalcSome возвращает тип int * int * int?


Имена принимающих переменных в строке вызова — забыл, нет? На них смотреть религия не позволяет?

G>>Разница только в четком отделении output параметров от input. И все. Это — удобно.


VD>А я с этим и не спорю. Кортыжу удобнее out/ref-параметров. Но это другой вопрос не устраняющий наличие проблемы потери информации.


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

G>>И это, кстати, главная и по сути единственная штука, которая меня постоянно напрягала при программировании на С++, после знакомства с ФЯ. Остальное можно пережить легко, если уметь обращаться с С++. Но эта мелочь — бесит.


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


Меня это не напрягало — я придерживался простой но строгой ownership policy, и не имел никаких проблем с ликами. Вообще. Могу рассказать, если интересно.
Re[4]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 21:55
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>>> res = fun( x, y, z );

G>>>( res, x, z ) = fun( y );

G>Вопрос. В каком из вариантов тебе при чтении кода вызова (не точки реализации, которая в обоих случаях одна), тебе проще понять, что входной параметр, что — выходной, а что является побочным эффектом?


Он имел в виду, что функцию ты описываешь один раз и если накосячил с именами параметров, то только в ней. А вызваешь ты ее 100 раз и накосячить с именами можешь неограниченно количество раз:

def (res, x, z) = fun( y );
...
def (x, res, z) = fun( y );
def (res, x, z) = fun( y );


На практике (на моей) это не часто приводит к проблема. Но потенциальная опасность есть. Шарповские анонимные типы решают эту проблему, но к сожалению у Майкрософт в последнее время огромные проблемы с последовательностью. По сути анонимные типы — это записи (кортежи с именованными полями). Если CLR умел их поддерживать, то достаточно было бы придумать красивый синтаксис описания анонимного типа (что-то вроде описания списка параметров метода, например), то проблема бы и не возникла. Но в МС почитали, что ради такой фигни как стройность флагманского языка менять CLR не стоит. Добавили, правда, что когда будут менять CLR, то исправят ситуацию. 4-й фрэймворк как раз и есть изменение CLR, но практически 100%, что и в нем не появится поддержки анонимных типов.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 21:56
Оценка: 57 (2)
Здравствуйте, VladD2, Вы писали:

VD>Сам использую кортежи постоянно. Но кортежий в 5 элементов стараюсь избегать (чисто интуитивно). Да и по больше части спользую котрежи внутри реализаций, так что бы на ружу они не торчали.


VD>Кстати, очень помогают локальные функции.


Все правильно говоришь, я делаю так же. Кстати, кортежи из 5 элементов не проблема, если ( x, y, ( z, a, b ) ). Кортеж в третьем элементе надо разматчить там, где он реально нужен. И все. Читабельность — сильно выигрывает.

Всяко лучше, чем функция с 5 аргументами, согласись. Опять же — имеем плюс от кортежей в читабельности.
Re[6]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.10.09 22:09
Оценка: +2
Здравствуйте, Gaperton, Вы писали:

G>Зато всегда есть имена принимающих переменных, которые названы так, что все понятно. Этого мало?


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

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

G>Кроме того, это имеет вообще значение только в случае, если возвращаемые значения тупла одного типа. Такое бывает редко, и обычно однозначно трактуется, скажем:


G>Прямоугольник, или линия — ( x1, y1, x2, y2 ).


Для прямоуголника я бы предпочел не котреж, а структуру с именами полей. А то кто знает, что ты там имел в виду "x2, y2" или "w, h"?

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


Не такая это и глупость. Особенно если тип можно было бы описать по месту.
Что ты имеешь против такого синтаксиса:
GetPersonInfo(id : int) : (Name:string * Addres:string * Age:int)

Ну, а дальше хочешь к полям обращайся через точку:

def p = GetPersonInfo(123);
... p.Age;

или декомпозицию в переменные делай, но чтобы при этом провлялись имена без учета регистра и наличия подчеркиваний в именах:
def (name, _, age) = GetPersonInfo(123); // OK
def (_, name, age) = GetPersonInfo(123); // Error

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

G>Имена принимающих переменных в строке вызова — забыл, нет? На них смотреть религия не позволяет?


Они могут быть перепутаны.

G>Существенно большая проблема — что ты не можешь понять, где у тебя output параметр, пока не заглянут в вызывающую функцию. Мешает читать код. Сильно мешает.


Этой проблемы нет во многих языка:
GetPersonInfo(out name, out addres, out age);

c# к ним относится.

G>Меня это не напрягало — я придерживался простой но строгой ownership policy, и не имел никаких проблем с ликами. Вообще. Могу рассказать, если интересно.


Вот это я и называю "дурацкие ретуальные действия и соблюдать кодекс чести". Проблем и я не имел, но тратил на это время которое мог бы потратить более интересно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 22:28
Оценка:
Здравствуйте, VladD2, Вы писали:

G>>Зато всегда есть имена принимающих переменных, которые названы так, что все понятно. Этого мало?


VD>Вопрос философский. Для кода реализации метода может и нормально. А в публичном интерфейсе я бы с удовольствием "запечатлел" бы имена и заставил бы компилятор контролировать их соответствие.


Я не говорю, что надо употреблять туплы в публично интерфейсе. Стилевое руководство Эрланга рекомендует применять в данном случае record-ы. Это важно, по причине, что на Эрланге написано больше всего промышленного кода с туплами на данный момент — наибольшая практика. Однако, возвращять туплы из пар элементов в публичном интерфейсе, к примеру:

{ Key, Value }

Считается вполне нормальным. И это правильно.

VD>В общем, это место где может появиться ошибка и его было бы не так уж трудно устранить. Но... про "но" я написал в соседней ветке.


На практике не наблюдал я таких ошибок. Длинный тупл — да (и то, в 95% ситуаций в тупле типы разные, и тайпчекер даст по рукам, в эрланге, кстати, он есть, я пользуюсь). Короткий — нет.

G>>Кроме того, это имеет вообще значение только в случае, если возвращаемые значения тупла одного типа. Такое бывает редко, и обычно однозначно трактуется, скажем:


G>>Прямоугольник, или линия — ( x1, y1, x2, y2 ).


VD>Для прямоуголника я бы предпочел не котреж, а структуру с именами полей. А то кто знает, что ты там имел в виду "x2, y2" или "w, h"?


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


VD>Не такая это и глупость. Особенно если тип можно было бы описать по месту.


Смысл какой, если этот тип часть сигнатуры функции, а имена переменных повторяют названия типов? Лишняя писанина.

VD>Что ты имеешь против такого синтаксиса:

VD>
VD>GetPersonInfo(id : int) : (Name:string * Addres:string * Age:int)
VD>

VD>Ну, а дальше хочешь к полям обращайся через точку:

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

G>>Имена принимающих переменных в строке вызова — забыл, нет? На них смотреть религия не позволяет?


VD>Они могут быть перепутаны.


Ситуация, когда в тупле значения одного типа идут подряд, их много, и они не имеют натурального порядка — крайне редка. Я ни разу с такой багой не сталкивался. Натуральный порядок, например — обозначение временного диапазона — сначала ты пишешь startTime, потом endTime, и по названиям легко понять, что ты их перепутал.

G>>Существенно большая проблема — что ты не можешь понять, где у тебя output параметр, пока не заглянут в вызывающую функцию. Мешает читать код. Сильно мешает.


VD>Этой проблемы нет во многих языка:

VD>
VD>GetPersonInfo(out name, out addres, out age);
VD>

VD>c# к ним относится.

Я не хочу тыкать в каждый вызов, и смотреть его сигнатуру, чтобы понять, что к чему. Это мешает. Я хочу читать код по диагонали — при поддержке это важно. Проблема в С# есть. В С++, к сведению, по кодстандарту CQG ты пишешь в имени аргумента префикс o_, i_, а также io_, что точно так же как и в твоем примере про C# "исправляет" ситуацию. Но я не хочу смотреть в сигнатуру. Вариант с туплами объективно лучше этих костылей во всех смыслах, не понимаю, с чем ты споришь. Это очевидно.

G>>Меня это не напрягало — я придерживался простой но строгой ownership policy, и не имел никаких проблем с ликами. Вообще. Могу рассказать, если интересно.


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


Я не тратил на это много времени. Один шаблонный класс MemberPtr для агрегации по указателю, который копирует контент при присваиваниях и конструкторе копирования, и удаляет в деструкторе — и все, никаких трат времени. Заодно — глядя на объявление класса, легко видно, где просто ссылка, а где агрегация. И никакого геморроя и потраченного времени.
Re[5]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 09.10.09 22:32
Оценка: +1
Здравствуйте, VladD2, Вы писали:

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


G>>>> res = fun( x, y, z );

G>>>>( res, x, z ) = fun( y );

G>>Вопрос. В каком из вариантов тебе при чтении кода вызова (не точки реализации, которая в обоих случаях одна), тебе проще понять, что входной параметр, что — выходной, а что является побочным эффектом?


VD>Он имел в виду, что функцию ты описываешь один раз и если накосячил с именами параметров, то только в ней. А вызваешь ты ее 100 раз и накосячить с именами можешь неограниченно количество раз:


VD>
VD>def (res, x, z) = fun( y );
VD>...
VD>def (x, res, z) = fun( y );
VD>def (res, x, z) = fun( y );
VD>


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

VD>
VD> res = fun( y, x, z );
VD>...
VD> x = fun( y, res, z );
VD> res = fun( x, z, y );
VD>


Разница — в чем? Вы о чем вообще говорите, люди? Бред какой-то.

VD>На практике (на моей) это не часто приводит к проблема. Но потенциальная опасность есть.


На моей практике это ни разу не приводило к большим проблемам, чем опасность перепутать порядок аргументов функции. Разницы — никакой. Вообще. Кроме пользы.
Re[6]: Использование tuple
От: maxkar  
Дата: 10.10.09 06:46
Оценка: +1
Здравствуйте, VladD2, Вы писали:

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


VD>>>Например, кому не ясно значение следующего метода list[T]?

VD>>>
VD>>>    public Partition (pred : T -> bool) : list [T] * list [T] 
VD>>>


M>>Мне не ясно, например. Кто первый в этой паре?


VD>Бывает. Наверно в школе с учебниками математики тоже проблемы были?

Да нет, с учебниками проблем не было. Особенно с учебниками математики. Я по ней олимпиадник был и пару раз участвовал в отборах на международный этап
С учебниками в общем случае чуть проще — там вроде бы практически все функции возвращают одно значение (одного "типа", а не в смысле однозначная/многозначная функция). Да и функций от двух и более аргументов вроде бы не слишком много. Т.е. подобных проблем нет.

M>> То, что соответствует предикату или то, что не соответствует?


VD>Разумеется сначала идут те что соответствуют, потом те что не соответствуют.


Почему разумеется? Вот есть у нас список [1,2,3,4,5,6]. И есть какой-то предикат p. Как разделять будем? Да очень просто — читаем список слева направо и все, на что p дает true, вычеркиваем и записываем в другой спискок. Ну и получим в результате что-то вроде [1,2,5][3,4,6]. Ну так здесь первое — это то, что фильтру не соответствуют. И "логических ошибок" я здесь не вижу. Да, вопросы "зачем так делать" есть, но противоречий показать не получится. В результате у кого-то из читающих могут возникнуть проблемы. И на больших командах вероятность неправильного понимания хотя бы у одного члена возрастает. Учитывая, что большинство программистов читать документацию не любит и то, что многие даже не "тестируют простейшие случаи", мы получим место для бага.

M>>И, самое интересное. Как я должен рассуждать, чтобы получить "правильный" ответ? Документацию я специально не смотрел и на шарпе не пишу.


VD>А как ты рассуждаешь когда используешь функцию Filter или Where? А в друг результат будет обратный предикату?


Вот если я использую Where, то рассуждаю просто — collection.where(predicate). Очевидно, это "коллекция", "где" выполняется "предикат". А с фильтром я не рассуждаю, я смотрю (или вспоминаю) документацию. Как раз потому, что не понятно, что именно должно вернуть colleciton.filter(predicate). И если посмотреть словари английского языка (я смотрел и переводы, и толковые словари), выяснится, что "filter something" имеет значение "remove something". Еще видел определение "pass through filter", но там в определении существительного "filter" тоже было "to remove". И вот здесь то как раз огромное место для путаницы, что определяет предикат — то, что нужно "отфильтровать", или то, что получить в результате.

VD>В реальном коде все выглядеть примерно так:

VD>
VD>def (fresh, old) = data.Partition(x => x.DateTime.Year >= DateTime.Now.Year);
VD>...
VD>

Угу. И если мне такой код попадется на review, я как раз пойду смотреть, в каком же порядке data.Partition() возвращает результат. Потому что по вышеописанным причинам здесь у писателя может быть ход мысли, отличный от хода мысли автора функции. Вообще, это проблема в первую очередь для прикладного кода, а не для библиотек. Библиотек все же не так много и их приходится знать (т.е. я буду помнить, что же делает библиотечный partition). А вот запоминать кучу дополнительных контрактов для "прикладного" кода ну совсем не хочется.

P.S. И вне зависимости от контракта метода data.Partition, я бы не оставил последний пример в коде в том виде, в котором он есть. Скорее всего, это было бы гневное письмо к автору с вопросом о том, что же именно делает эта строчка кода. Потому что в old могут оказаться x, у которых datetime идут раньше (не позже), чем у некоторых x из fresh. И что самое обидное, этот баг будет проявляться на новый год (когда DateTime.Now.Year изменится во время вычисления Partition). А мне ну никак не хочется быть выдернутым в новогоднюю ночь фиксить критический баг. Да и в новогодние каникулы тоже не хочется.
Re[8]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 10.10.09 07:44
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Я не хочу тыкать в каждый вызов, и смотреть его сигнатуру, чтобы понять, что к чему. Это мешает. Я хочу читать код по диагонали — при поддержке это важно. Проблема в С# есть. В С++, к сведению, по кодстандарту CQG ты пишешь в имени аргумента префикс o_, i_, а также io_, что точно так же как и в твоем примере про C# "исправляет" ситуацию. Но я не хочу смотреть в сигнатуру.


Ты не понял — в шарпе out указывается и при вызове тоже.
... << RSDN@Home 1.2.0 alpha 4 rev. 1245 on Windows 7 6.1.7600.0>>
AVK Blog
Re[8]: Использование tuple
От: Mirrorer  
Дата: 10.10.09 07:56
Оценка:
Здравствуйте, Gaperton, Вы писали:


G>Я не говорю, что надо употреблять туплы в публично интерфейсе. Стилевое руководство Эрланга рекомендует применять в данном случае record-ы.


Имеется ввиду эта штука или что-то другое ?
Re[8]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 11:59
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Ситуация, когда в тупле значения одного типа идут подряд, их много, и они не имеют натурального порядка — крайне редка. Я ни разу с такой багой не сталкивался. Натуральный порядок, например — обозначение временного диапазона — сначала ты пишешь startTime, потом endTime, и по названиям легко понять, что ты их перепутал.


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

G>Я не хочу тыкать в каждый вызов, и смотреть его сигнатуру, чтобы понять, что к чему. Это мешает. Я хочу читать код по диагонали — при поддержке это важно. Проблема в С# есть. В С++, к сведению, по кодстандарту CQG ты пишешь в имени аргумента префикс o_, i_, а также io_, что точно так же как и в твоем примере про C# "исправляет" ситуацию. Но я не хочу смотреть в сигнатуру. Вариант с туплами объективно лучше этих костылей во всех смыслах, не понимаю, с чем ты споришь. Это очевидно.


Ты не понял. Язык заставляет писать out/ref при вызове. Если в функции есть out-параметр, то ты не можешь не написать при вызове "out variable", так как в противном случае компилятор выдаст ошибку. Сделано это как раз, чтобы сразу было понятно, что параметр возвращает значение.

Вот такие решения (в дизайне языка) мне нравятся. Это уже не соглашение, а правило которое нельзя обойти. Жаль, что оно сделано для весьма неудобной фичи.

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


G>Я не тратил на это много времени.


А вот это, извини за резкость, сказки. Иначе скорость программирования на любом языке была бы одинакова, так как ко всему можно привыкнуть и "не иметь проблем".

G>Один шаблонный класс MemberPtr для агрегации по указателю, который копирует контент при присваиваниях и конструкторе копирования, и удаляет в деструкторе — и все, никаких трат времени. Заодно — глядя на объявление класса, легко видно, где просто ссылка, а где агрегация. И никакого геморроя и потраченного времени.


Не, батенька. Время ты тратишь при проектировании. Ты вынужден создавать более детальные решения. Вынужден продумывать иделогию владения и т.п. Плюс ты вынужден заниматься ритуальными приседаниями.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 12:11
Оценка: +1
Здравствуйте, Gaperton, Вы писали:

G>Да ну. А без туплов, типа, ты не можешь 100 раз накосячить так:


VD>>
VD>> res = fun( y, x, z );
VD>>...
VD>> x = fun( y, res, z );
VD>> res = fun( x, z, y );
VD>>


Могу. Но разобраться будте проще. Так как мне будте достаточно поглядеть на хинт в IDE и увидить несоответствие.

G>Разница — в чем? Вы о чем вообще говорите, люди? Бред какой-то.


Мы говорим о статически типизированных языках имющих офигительную поддержку IDE. Она в сочетании с языковыми решениями позволяет делать меньше ошибок. Тюплы тут несколько не вписываются в картину. Еще раз повторюсь, что лично мне, хотя я крайне неаккуратный человек, это не очень мешает, но я уверен, что если мы говорим о действительном массовом применении, то это не самое лучше решение которое будет источником ошибок. Другое дело, что применение циклов, к примеру, не чем не меньший источник ошибок. Но люди к ним привыкли и не обращают на это внимание. А на кортежи обращают, так как вещь новая, а все новые вещи у разумных людей, по началу взывают опасения.

Так вот замена (точнее дополнение к) циклам — ФВП. Таким же дополнением к кортежам должны быть записи.
Ты сам говоришь, что в эрланге есть записи и их рекомендуется экспортировать из публичных функций.
А вот в дотнете (заметь, не шарпе, а именно дотнете) нет аналога записей. Конечно можно объявить класс, но вот беда, класс не будет идентичен такому же классу объявленному другой сборке. К тому же класс нужно явно объявлять, а в шарпе — это требует написания довольно большого объема кода (конструкторы нужно описывать, права доступа задавать...), что неудобно. Отличным решением было бы ввести в дотнет записи. Тогда в языке можно было бы ввести инлайн-синтаксис описания этих самых записей и многие проблемы ушли бы сами собой.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 12:24
Оценка: +1 -1
Здравствуйте, maxkar, Вы писали:

M>Да нет, с учебниками проблем не было. Особенно с учебниками математики. Я по ней олимпиадник был и пару раз участвовал в отборах на международный этап


Вот и тут не будет. У тебя же не бывает проблем с чтение кода вроде "f(a, b)"? Ну, и с "def (a, b) = f();" не будет.

M>Почему разумеется?


Потому что это интуитивно. Везде в ФЯ ты будешь встречать конструкции вроде "head :: tail". Так что о том, что идет вначале ты даже задумываться не будешь. А если вдруг задумаешся, то есть описание метода доступное как в коментариях, так и в интеллисенсе (в IDE).

M>Вот есть у нас список [1,2,3,4,5,6]. И есть какой-то предикат p. Как разделять будем? Да очень просто — читаем список слева направо и все, на что p дает true, вычеркиваем и записываем в другой спискок.


Это какая-то извращенная императивная логика. Функциональная логика будет такая:
1. Для пустого списаа [] вернем ([], []).
2. Для не пустого списка x :: xs присоединим x к первому списку если x удовлетворяет предикату и ко второму если нет.
3. Повторим рекурсивно операцию для остатка списка (xs).
А вот как эта реализация записана на немерле:
    /**
     * Partitions a list into two sublists according to a predicate.
     */
    public Partition[T] (l : list [T], pred : T -> bool) : list [T] * list [T]
    {
      def loop (l : list [T], sat : list [T], notsat : list [T])
      {
        match (l) {
          | h :: t => 
            if (pred (h)) 
              loop (t, h :: sat, notsat)
            else 
              loop (t, sat, h :: notsat)
          | [] => (Rev (sat), Rev (notsat))
        }
      }
      loop (l, [], [])  
    }


M>Вот если я использую Where, то рассуждаю просто — collection.where(predicate). Очевидно, это "коллекция", "где" выполняется "предикат". А с фильтром я не рассуждаю,


А, ну, ясно решил докопаться до радио. Продолжай в том же духе.
Методом Select не пользуйся, так как до него тоже можно докапываться. Он же занимается отображением или проекциец, а не как не выбором. SQL тоже в плохиши запиши...
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.10.09 12:34
Оценка: :)
Здравствуйте, maxkar, Вы писали:

VD>>А как ты рассуждаешь когда используешь функцию Filter или Where? А в друг результат будет обратный предикату?


M>Вот если я использую Where, то рассуждаю просто — collection.where(predicate). Очевидно, это "коллекция", "где" выполняется "предикат". А с фильтром я не рассуждаю, я смотрю (или вспоминаю) документацию. Как раз потому, что не понятно, что именно должно вернуть colleciton.filter(predicate). И если посмотреть словари английского языка (я смотрел и переводы, и толковые словари), выяснится, что "filter something" имеет значение "remove something". Еще видел определение "pass through filter", но там в определении существительного "filter" тоже было "to remove". И вот здесь то как раз огромное место для путаницы, что определяет предикат — то, что нужно "отфильтровать", или то, что получить в результате.


VD>>В реальном коде все выглядеть примерно так:

VD>>
VD>>def (fresh, old) = data.Partition(x => x.DateTime.Year >= DateTime.Now.Year);
VD>>...
VD>>

M>Угу. И если мне такой код попадется на review, я как раз пойду смотреть, в каком же порядке data.Partition() возвращает результат.

На самом деле программист обязан знать, что делает функция или метод, так как они являются абстракцией. Вопрос в том, что какие-то функции могут получить интуитивные имена, а какие-то нет (не будем же мы давать функциям имена состоящие из 40 слов?). И это не зависит от того используются ли тюплы или нет. Ты точно так же поцдешь смотреть, что делает функция если она возвратит одно значение. Да что там одно? Даже если она вообще ничего не возвращает, ты можешь "пойти". Ну, а вопрос интуитивности это совершенно отдельный вопрос. Для меня Filter и Partition совершенно интуитивно понятные названия определяющие как параметры, так и вовращаемые значения. Если тебе они не ясны, ну, что что или запомнишь, или будешь каждый раз читать документацию.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 11.10.09 14:06
Оценка:
Здравствуйте, AndrewVK, Вы писали:

G>>Я не хочу тыкать в каждый вызов, и смотреть его сигнатуру, чтобы понять, что к чему. Это мешает. Я хочу читать код по диагонали — при поддержке это важно. Проблема в С# есть. В С++, к сведению, по кодстандарту CQG ты пишешь в имени аргумента префикс o_, i_, а также io_, что точно так же как и в твоем примере про C# "исправляет" ситуацию. Но я не хочу смотреть в сигнатуру.


AVK>Ты не понял — в шарпе out указывается и при вызове тоже.


Это несколько меняет дело. Но только "несколько". Как это поможет мне отличить выходной параметр от параметра io_ — с побочным эффектом? Туплы решают именно эту проблему. Кроме того, с туплами это выглядит элементарно лучше и естественнее, чем префиксы. Разумеется, при поддержке паттерн-матчинга на них.
Re[9]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 11.10.09 14:08
Оценка:
Здравствуйте, Mirrorer, Вы писали:

G>>Я не говорю, что надо употреблять туплы в публично интерфейсе. Стилевое руководство Эрланга рекомендует применять в данном случае record-ы.


M>Имеется ввиду эта штука или что-то другое ?


Эта штука, пункт 6.1.

The record features of Erlang can be used to ensure cross module consistency of data structures and should therefore be used by interface functions when passing data structures between modules.
Re[10]: Использование tuple
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.10.09 17:12
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Это несколько меняет дело. Но только "несколько". Как это поможет мне отличить выходной параметр от параметра io_ — с побочным эффектом?


У которого с побочным эффектом — у него модификатор не out, а ref.
... << RSDN@Home 1.2.0 alpha 4 rev. 1245 on Windows 7 6.1.7600.0>>
AVK Blog
Re[5]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 12.10.09 15:58
Оценка:
VD>Он имел в виду, что функцию ты описываешь один раз и если накосячил с именами параметров, то только в ней. А вызваешь ты ее 100 раз и накосячить с именами можешь неограниченно количество раз:

Именно это я и хотел сказать. Спасибо.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[10]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.10.09 16:39
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Это несколько меняет дело. Но только "несколько". Как это поможет мне отличить выходной параметр от параметра io_ — с побочным эффектом?


io — обозначаются ref и ref тоже нужно указывать при вызове.

G>Туплы решают именно эту проблему. Кроме того, с туплами это выглядит элементарно лучше и естественнее, чем префиксы. Разумеется, при поддержке паттерн-матчинга на них.


На самом деле ref и out попросту не удобны. Для них нужно объявлять отдельные переменные, да и вообще не красиво. Такие феньки как вложение вызовов функций уже не прокатывают. Плюс есть еще одна проблема. ref и out-параметры требуют, чтобы выражения были lvalue, так что даже если тебе возвращаемое значение не интересно ты не сможешь в ref-параметр передать значение фунции (без промежуточной переменной).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Использование tuple
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 12.10.09 22:24
Оценка: 56 (1) +1
Здравствуйте, Gaperton, Вы писали:

G>Ситуация, когда в тупле значения одного типа идут подряд, их много, и они не имеют натурального порядка — крайне редка. Я ни разу с такой багой не сталкивался. Натуральный порядок, например — обозначение временного диапазона — сначала ты пишешь startTime, потом endTime, и по названиям легко понять, что ты их перепутал.


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

  [b,bint,r,rint,stats] = regress(y,X,alpha)


В этом примере b — вектор точечных оценок коэффициентов линейного уравнения регрессии, bint — матрица интервальных оценок параметров линейной регрессии, r — вектор остатков, rint — матрицу доверительных интервалов остатков. Получаем, что все пять параметров это вектора/матрицы, которые вполне могут кодироваться одним типом

Похожие примеры из той же предметной области:

  [p,table,stats,terms] = anovan(...) 
  [h,atab,ctab,stats] = aoctool(...)
  [ndim,prob,chisquare] = barttest(x,alpha)
  [A,B,r,U,V,stats] = canoncorr(X,Y)
  [p,Cp,Cpk] = capable(data,specs)
  [class,err,posterior,logp] = classify(...)
Re[11]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 12.10.09 23:23
Оценка:
Здравствуйте, AndrewVK, Вы писали:

G>>Это несколько меняет дело. Но только "несколько". Как это поможет мне отличить выходной параметр от параметра io_ — с побочным эффектом?


AVK>У которого с побочным эффектом — у него модификатор не out, а ref.


А. Ну тогда действительно все в порядке. Запись с туплом и матчингом, однако, все равно удобнее — в данном случае достаточно одного модификатора ref. И все, никакого мусора. Синтаксический оверхэд (тм), сам понимаешь. Иметь возвращаемые значения слева от знака присваивания, где они и должны быть, более естественно, чем расставлять модификаторы-подсказки.

Впрочем, этим польза от туплов не ограничивается. Тупл также позволяет группировать аргументы функции, что сильно помогает в читабельности как вызовов, так и (особенно) тела функции, если применять с умом. Тупл может с той же целью применяться в теле функции.
Re[9]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 12.10.09 23:32
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Это зависит от предметной области. В MATLAB таких функций много. Я запускаю на выполнение математический расчет, который помимо основного результата может вывести еще кучу вспомогательных (побочных) результатов. Например,


M>
M>  [b,bint,r,rint,stats] = regress(y,X,alpha)
M>


M>В этом примере b — вектор точечных оценок коэффициентов линейного уравнения регрессии, bint — матрица интервальных оценок параметров линейной регрессии, r — вектор остатков, rint — матрицу доверительных интервалов остатков. Получаем, что все пять параметров это вектора/матрицы, которые вполне могут кодироваться одним типом


Ну вот я пишу сигнатуру типа подобного вызова — синтаксис Эрланга.

-spec regress( y::vector(), X::vector(), alpha::float() )
    -> { b::vector(), bint::matrix(), r::vector(), rint::matrix(), stats::any() }


Все, тайпчекер даст по рукам при ошибке. И в чем именно проблема с таким туплом, можно поинтересоваться? Вот так — было бы сильно легче, да?

-spec regress( y::vector(), X::vector(), alpha::float(), bint::matrix(), r::vector(), rint::matrix(), stats::any() )
    -> b::vector()


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

-spec regress( y::vector(), X::vector(), alpha::float() )
    -> { b::vector(), bint::matrix(), { r::vector(), rint::matrix(), stats::any() } }
Re[9]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 12.10.09 23:54
Оценка: +1
Здравствуйте, VladD2, Вы писали:

G>>Ситуация, когда в тупле значения одного типа идут подряд, их много, и они не имеют натурального порядка — крайне редка. Я ни разу с такой багой не сталкивался. Натуральный порядок, например — обозначение временного диапазона — сначала ты пишешь startTime, потом endTime, и по названиям легко понять, что ты их перепутал.


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


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

VD>Вот такие решения (в дизайне языка) мне нравятся. Это уже не соглашение, а правило которое нельзя обойти. Жаль, что оно сделано для весьма неудобной фичи.


Да пожалуйста. А мне нравятся удобные, простые и красивые решения — вроде туплов с матчингом.

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


G>>Я не тратил на это много времени.


VD>А вот это, извини за резкость, сказки. Иначе скорость программирования на любом языке была бы одинакова, так как ко всему можно привыкнуть и "не иметь проблем".


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

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

G>>Один шаблонный класс MemberPtr для агрегации по указателю, который копирует контент при присваиваниях и конструкторе копирования, и удаляет в деструкторе — и все, никаких трат времени. Заодно — глядя на объявление класса, легко видно, где просто ссылка, а где агрегация. И никакого геморроя и потраченного времени.


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


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

VD> Плюс ты вынужден заниматься ритуальными приседаниями.


А вот здесь ты ошибся — я не религиозен.
Re[10]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 00:22
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Удивительно, и как люди только на мэйнстримовом С программят — одни ведь соглашения, которых надо придерживаться.


Херовенько. Иногда вместо решения основной задачи часами (а то и днями) баги ищут. Некоторые баги живут по 10 лет. Про ассемблер вообще молчу.

G> Ты против туплов, что-ли?


Я "за".

G>Ты ясно скажи, с чем ты не согласен, а то я перестал понимать. Тон, вроде, дискуссионный, споришь с чем-то.


Не согласен с тем, что нет проблем. Проблемы есть, но они не значительны. Рядом в теме я описал свои идеи на этот счет.

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

VD>>Вот такие решения (в дизайне языка) мне нравятся. Это уже не соглашение, а правило которое нельзя обойти. Жаль, что оно сделано для весьма неудобной фичи.


G>Да пожалуйста. А мне нравятся удобные, простые и красивые решения — вроде туплов с матчингом.


Повторяться не буду. Мне тоже нравится. Но я идеалист, в некотором роде.

VD>>А вот это, извини за резкость, сказки. Иначе скорость программирования на любом языке была бы одинакова, так как ко всему можно привыкнуть и "не иметь проблем".


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


А что неприличного в моих словах?

G>Я говорю не про "любой язык", и не про абстрактную скорость, а про то, что конкретно я не тратил много времени на управление памятью в С++. Мне виднее, сколько времени я тратил, и твои соображения про "любой язык" ничего не меняют.


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

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


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

VD>> Плюс ты вынужден заниматься ритуальными приседаниями.


G>А вот здесь ты ошибся — я не религиозен.


А ритуальные приседания — это не религия. Это — шаманство.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 06:24
Оценка: +1
Здравствуйте, Gaperton, Вы писали:
G>Зато всегда есть имена принимающих переменных, которые названы так, что все понятно. Этого мало?
Конечно мало. Возвращаясь к примеру с вызовом обычного метода — у тебя что, всегда имена фактических аргументов совпадают с именами параметров?
Если убрать имена параметров из стандартной библиотеки, оставив только _1, _2, _3, то как-то трудновато будет разобраться с тем, что происходит, не так ли?

G> Кроме того, это имеет вообще значение только в случае, если возвращаемые значения тупла одного типа. Такое бывает редко, и обычно однозначно трактуется, скажем:

G>Прямоугольник, или линия — ( x1, y1, x2, y2 ).
Отлично. Осталось понять, кто из них горизонтальный, а кто вертикальный. И x2 там в третьем члене или width.
G>Диапазон времени — ( startTime, endTime ).
Или всё-таки startTime, timeSpan?

G>Имена принимающих переменных в строке вызова — забыл, нет? На них смотреть религия не позволяет?

А если их перепутали?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 06:24
Оценка: 10 (1)
Здравствуйте, VladD2, Вы писали:
VD>Не такая это и глупость. Особенно если тип можно было бы описать по месту.
VD>Что ты имеешь против такого синтаксиса:
VD>
VD>GetPersonInfo(id : int) : (Name:string * Addres:string * Age:int)
VD>

VD>Ну, а дальше хочешь к полям обращайся через точку:

VD>
VD>def p = GetPersonInfo(123);
VD>... p.Age;
VD>


VD>или декомпозицию в переменные делай, но чтобы при этом провлялись имена без учета регистра и наличия подчеркиваний в именах:

VD>
VD>def (name, _, age) = GetPersonInfo(123); // OK
VD>def (_, name, age) = GetPersonInfo(123); // Error
VD>

VD>Ну, и естественно типы описанные таким образом в разных библиотеках (в том числе и с динамической подгрузкой) считались идентичными.
Я бы тут постарался развернуть вызов задом наперёд. Так, чтобы code completion мне помогал. Что-то типа
GetPersonInfo(123) => (name, _, age);
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 06:24
Оценка: +2
Здравствуйте, VladD2, Вы писали:
VD>На самом деле ref и out попросту не удобны. Для них нужно объявлять отдельные переменные, да и вообще не красиво. Такие феньки как вложение вызовов функций уже не прокатывают. Плюс есть еще одна проблема. ref и out-параметры требуют, чтобы выражения были lvalue, так что даже если тебе возвращаемое значение не интересно ты не сможешь в ref-параметр передать значение фунции (без промежуточной переменной).
Кроме того, ref и out не поддерживают вариантность (из-за многократного присваивания).
Поэтому ты не можешь, к примеру, использовать object в качестве out-переменной для параметра, который тебе не нужен.
Т.е. вот такой синтаксис работает:
Mammal m = tigers["Sam"]; // var tigers = new Dictionary<string, Tiger>();

а вот такой — нет:
Mammal m;
if(tigers.TryGetValue("Sam", out m))
  return m;
else 
  return null;
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Использование tuple
От: maxkar  
Дата: 13.10.09 10:19
Оценка:
Здравствуйте, VladD2, Вы писали:

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


M>>Да нет, с учебниками проблем не было. Особенно с учебниками математики. Я по ней олимпиадник был и пару раз участвовал в отборах на международный этап


VD>Вот и тут не будет. У тебя же не бывает проблем с чтение кода вроде "f(a, b)"? Ну, и с "def (a, b) = f();" не будет.


Тут в соседней ветке Sinclar привел хороший пример с прямоугольниками, так что могу утверждать: даже при чтении (проверке!) вызова функции в виде f(a, b) могут быть проблемы. Касается как раз тех самых прямоугольников. Например, g.drawRect(x1, y1, x2, y2) является местом потенциальной ошибки (опять же, нужно знать библиотеку или читать документацию). По сравнению с ним вызов g.drawRect(rect) гораздо читабельнее. Да, там нужно проверять, что rect инициализирован правильно, но он то инициализируется через rect.setWidth(width) или rect.setX2(x2), что читается и проверяется легче (глаз цепляется за rest.setWidth(x2) или rect.setX2(width)).

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

Теперь перейдем к математике. Стоит заметить, что выражения вида (a, b) = f() встречаются редко. Обычно идет a = f_a(), b = f_b(). Что-то не помню, чтобы из математических функций возвращалиь "пары и более объектов". Вектора — да, но это "объект вектор". Возврат прямоугольника скорее всего выглядел бы как "Пусть прямоугольник R — это .... Функция f находит прямоугольник r1, такой, что ...". Т.е. в математических текстах практически везде мы будем иметь "класс" (или запись с именованными полями). Вполне вероятно, что "кортежи" можно будет встретить только в областях, непосредственно соприкасающихся с IT. Это области, изучающие алгоритмы и их сложность. В этом случае возврат кортежа вместо класса или вызова нескольких функций может давать преимущества (например, по времени выполнения). В примере Partition, например, в математическом тексте, скорее всего было бы x1 = {x in X : p}, x2 = {x in X : (not p} (или x2 = X\x1). Если не брать в расчет сложность, я не вижу, где бы эта запись проигрывала записи с partition, возвращающей пару. Данная запись чуть длиннее, но явно говорит, что и где создается.

Второе замечание про математику — сравнивать типичную программу с учебником не совсем корректно. В них очень сильно различается акцент на правильности понимания читателем текста. Так, типичный учебник помимо определения содержит еще примеры и задачи. И цель этих примеров и задач — определить, что ученик понял определение/термин/абстракцию неправильно и откорректировать понимание. Причем такой "вспомогательный текст" составляет большую часть учебника. Сами формальные определения составляют меньшую часть. В типичной программе — наоборот. Большая часть — формальные определения (через детали реализации), меньшая — документация (если вообще есть). Это ближе к справочнику математических функций, который для большинства функций просто приводит их определение через другие функции. Ну да, понять, что же обозначает функция (ее физический смысл) можно, но гораздо тяжелее. Так и с программами — комментариев мало или они низкого качества, основная часть — определения функций. Встречаются исключения, но очень редко.

M>>Почему разумеется?


VD>Потому что это интуитивно. Везде в ФЯ ты будешь встречать конструкции вроде "head :: tail". Так что о том, что идет вначале ты даже задумываться не будешь. А если вдруг задумаешся, то есть описание метода доступное как в коментариях, так и в интеллисенсе (в IDE).

Конструкция вида head :: tail вполне нормальна. Она применяется к списку, который имеет порядок элементов. И "разрезать" список на части с получением частей (не только по одному элементу, а вообще в фиксированной позиции) как раз просто — у этих частей есть неявное отношение "порядка", определяемое "порядком" в исходном списке. В связи с этим интересный вопрос — какую сигнатуру должен иметь кортеж, который возвращает функция, откусывающая последний элемент списка. Должно ли это быть (list a, a) (из отношения порядка) или (a, list a) ("скаляр" и не "скаляр")? Но это мелочь на самом деле, вопрос на "консистентность" API, ни одно из решений не ухудшает читаемость (типы разные).

M>>Вот есть у нас список [1,2,3,4,5,6]. И есть какой-то предикат p. Как разделять будем? Да очень просто — читаем список слева направо и все, на что p дает true, вычеркиваем и записываем в другой спискок.


VD>Это какая-то извращенная императивная логика. Функциональная логика будет такая:

VD>1. Для пустого списаа [] вернем ([], []).
VD>2. Для не пустого списка x :: xs присоединим x к первому списку если x удовлетворяет предикату и ко второму если нет.
VD>3. Повторим рекурсивно операцию для остатка списка (xs).
А на втором шаге — та же самая "извращенная" императивная логика. И ведь совершенно нет объяснения тому, почему на этом самом шаге к первому списку х присоединяется тогда, когда предикат выполняется, а не наоборот. А ведь если изменить это правило, то получится мой вариант

Теперь вспомним про "отношение порядка элементов списка" и то, что кортеж является упорядоченной парой (в данном случае). Для большинства функций, работающих со списком, порядок на результате (в возвращаемых кортежах) логично выводится (индуцируется) из отношения порядка на исходном списке (аргументе). Ну тот же partition по позиции в списке, например. Попытка распространить "порядок" исходного списка на partition по предикату дает еще два интересных упорядочивания результата.
Первый — первый элемент кортежа содержит первый элемент исходного списка.
Второй — последний элемент кортежа содержит последний элемент списка.
Такое упорядочивание (допустим для определенности первый вариант) очень хорошо. Оно, например, очень хорошо обобщается с pred : T -> bool на pred : T -> T1. Семантика "обобщенного partition" — разбить список элементов на классы эквивалентности согласно предиката pred. Очевидно, что в общем случае среди этих классов эквивалентности никакого отношения порядка нет. И в этом случае если мы хотим какой-то порядок результатов, мы должны создавать его исходя из исходного состояния списка. Вполне логичен (с "императивной" точки зрения) первый вариант (порядок классов эквивалентности совпадает с порядком первых элементов этих классов в исходном списке). В ФП с рекурсией и т.п. это может быть второй вариант (определяется последним элементом в классе).
Ладно, вернемся к случаю T -> bool (T1 = bool). Допустим, на T1 есть "отношение порядка" (не знаю, есть ли оно на самом деле в C# и nemerle). В большинстве языков False < True (проверил haskell и ocaml, знаю что в C/C++, если не ошибаюсь, в paskall так же). Тогда логично предположить, что что порядок списков в результате совпадает с порядком на T1 (= bool), а сам "класс эквивалентности" определяется предикатом. Ну в "императивной логике" мы перечисляем элементы T1 и для них выбираем значения из T. Это, кстати, даст вариант, когда первыми идут значения, не соответствующие предикату.
Только вот мы видим, что и последний случай — не наш (ну либо в c#/nemerle отношение порядка на bool определено сильно нестандартным образом). Т.е. мы получили "специальное решение" для какого-то очень частного случая, который не выводится из общих случаев. Все таки мне кажется, что в существующей реализации такой порядок "получился случайно", а не следует из каких-либо других соображений.

После всего того, что написал, мне кажется наиболее логичным вариант упорядочивания по первому элементу в списке. Хотя да, я предполагал, что наиболее вероятным будет ответ "сначала то, что соответствует предикату, затем остаток". Но делал это не исходя из каких-либо соображений о списках и т.п., а из "ситуационной логики" автора функции, которому нужно было получить "список, соответствующий предикату и все остальное". Только вот функцию он назвал partition, а не по тому, что она на самом деле делает. И, кстати, сделал эту функцию недостаточно обобщенной (см. выше, partition в общем случае вполне работает на pred : T -> T1). Да и плохо то, что такая "телепатия" может не сработать в каком-нибудь случае и будет обидно. А учить все "сложившиеся случайности" не очень хочется.
VD>А вот как эта реализация записана на немерле:
VD>
VD>    /**
VD>     * Partitions a list into two sublists according to a predicate.
VD>     */
VD>    public Partition[T] (l : list [T], pred : T -> bool) : list [T] * list [T]
VD>    {
VD>

Ой! У меня на ревью такая конструкция во всех случаях получает "штамп" "не reusable, провоцирует создание велосипедов". Причина проста — не описан порядок возвращаемых значений. Дополнительное условие — partition реализуется элементарно своими силами. Я уже выше привел кучу рассуждений о том, какие же порядки в результате бывают и что нет никаких аргументов за один из них. В документации порядок значений в паре не описан, поэтому intellisence и ide мне ничем помочь не смогут. Использовать телепатию рискованно — можно ошибиться. Читать чужой код особого смысла нет. Я напишу свой вариант, при этом потеряю немного, но у меня таки будет уже "велосипед", который можно использовать в IDE (он дает подсказку, например). Ну и свой вариант я какое-то время буду помнить. А когда забуду — напишу еще один, они же быстро пишутся.

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

M>>Вот если я использую Where, то рассуждаю просто — collection.where(predicate). Очевидно, это "коллекция", "где" выполняется "предикат". А с фильтром я не рассуждаю,


VD>А, ну, ясно решил докопаться до радио. Продолжай в том же духе.

А радио то бывает разное. Радиоприемники, например, чувствительностью отличаются. А читаемость при поиске бага или добавлении фичи — важный для меня момент. Хорошая читаемость позволяет не просматривать лишние методы, открывать или вспоминать документацию и т.п. Меньше скачков внимания, меньше вероятности ошибиться. Кстати, я уже выше "порассуждал с фильтром", получил еще 3 варианта, один из которых кажется более логичным и более "обобщаемый", чем вариант в библиотеке. Так что практика показывает, что рассуждать на "типичном" коде опасно.
VD>Методом Select не пользуйся, так как до него тоже можно докапываться. Он же занимается отображением или проекциец, а не как не выбором. SQL тоже в плохиши запиши...
Ну так а разве "выбор" не является проекцией? Да и термин понятнее большинству. Ну чем отличается "выбор по условию" и "проекция по условию"? Первое поймет больше народу. А второе бывает полезно в определенных контекстах для устранения неоднозначностей, но там, где однозначностей не возникает — не обязательно.
Да и если мне не изменяет память, select как раз таки не является проекцией в смысле реляционной алгебры. Реляционная алгебра работает в терминах множеств (неупорядоченной структуры) кортежей, а select позволяет еще и сортировки указывать (т.е. возвращает не множество а список) А то ведь мне хочется иметь возможность применять order by к результату join'а, например, без лишних select'ов.
Re[15]: Использование tuple
От: dr.Chaos Россия Украшения HandMade
Дата: 13.10.09 10:47
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>10% кода могут породить 90% багов И вообще, странно слышать аргумент про "требуют чуть более внимательного отношения" от человека предпочитающего статическую типизацию перед динамической из-за возможности получить ошибку приведения типов во время выполнения.


В Хаскеле есть newtype. Если его применить совместно с туплами перепутать будет невозможно.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Re[8]: Использование tuple
От: maxkar  
Дата: 13.10.09 10:48
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>На самом деле программист обязан знать, что делает функция или метод, так как они являются абстракцией. Вопрос в том, что какие-то функции могут получить интуитивные имена, а какие-то нет (не будем же мы давать функциям имена состоящие из 40 слов?). И это не зависит от того используются ли тюплы или нет. Ты точно так же поцдешь смотреть, что делает функция если она возвратит одно значение. Да что там одно? Даже если она вообще ничего не возвращает, ты можешь "пойти". Ну, а вопрос интуитивности это совершенно отдельный вопрос. Для меня Filter и Partition совершенно интуитивно понятные названия определяющие как параметры, так и вовращаемые значения. Если тебе они не ясны, ну, что что или запомнишь, или будешь каждый раз читать документацию.


Не может программист знать, что функция делает. Для библиотек еще можно такого требовать, но для кода организации — нет. Иначе ценность программиста в компании сильно определяется тем, что же из ее кода он знает. А это плохо и для организации, и для самого программиста. Для организации плохо тем, что менять программистов тяжело. Для программиста тем, что полученная информация совершенно не конвертируемая (в другой организации придется учить другой набор велосипедов). Чем меньше нужно учить — тем лучше. Да и при том обычно получается так, что функции и методы являются плохой абстракцией. Ну не заботятся типичные разработчики о том, чтобы выделить и логично описать эту абстракцию. Так получаются, например, функции, которые являются "шаблоном с параметрами". Т.е. это алгоритм, в который подставляются отдельные фрагменты. Сам по себе алгоритм (метод) никакой законченной абстракцией не является и хоть что-то начинает проявляться только тогда, когда появляются его аргументы (т.е. соответствующие куски кода).
Я согласен с тем, что хорошо бы было, если бы имена были "интуитивные" и что это не всегда получается. Также хорошо бы было, чтобы все приложение было организовано "логичным образом" и т.п. А у тюплов (кортежей?) здесь примерно та же проблема, что и у out-параметров. Они позволяют "усложнить" ответственность функции, сделать неинтуитивные вещи. Причем в них даже не надо писать out при использовании. Кроме того, тюплы позволяют использовать себя там, где по-хорошему стоило бы использовать "запись" с именованными полями. Т.е. их наличие в языке открывает дорогу к misuse при том, что облегчает не так много ситуаций.

Интуитивность — да, отдельный вопрос. Но здесь следует уметь отличать два варианта — "интуитивно очевидно, что" и "как несложно показать, из введенных нами концепций следует, что...". Первое подлежит обязательному документированию, второе — нет. В противном случае можно получить проблемы, если у кого-то интуиция работает по-другому (например, он немного в другой области работал) и без документированного момента он тоже решит, что "все очевидно".
Re[9]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.10.09 11:54
Оценка: 10 (1) +1
Здравствуйте, maxkar, Вы писали:

M>Тут в соседней ветке Sinclar привел хороший пример с прямоугольниками, так что могу утверждать: даже при чтении (проверке!) вызова функции в виде f(a, b) могут быть проблемы. Касается как раз тех самых прямоугольников. Например, g.drawRect(x1, y1, x2, y2) является местом потенциальной ошибки (опять же, нужно знать библиотеку или читать документацию). По сравнению с ним вызов g.drawRect(rect) гораздо читабельнее. Да, там нужно проверять, что rect инициализирован правильно, но он то инициализируется через rect.setWidth(width) или rect.setX2(x2), что читается и проверяется легче (глаз цепляется за rest.setWidth(x2) или rect.setX2(width)).

Не-не-не-не-не, Дэвид Блейн. Две ошибки.
1. В записи g.drawRect(x1, y1, x2, y2) мы достаточно хорошо защищены от ошибки мудрой IDE, которая по ctrl-space покажет нам имена соучастников рисования прямоугольника.
2. Вызов g.drawRect(rect) не лучше. Я вижу два варианта:

g.drawRect(Rect(x1, y1, x2, y2));

rect = new Rect();
rect.X1 = x1;
rect.X2 = x2;
rect.Width = x2-y2; //+1
rect.Height = y2-y1; //+1
g.drawRect(rect);

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

Если есть опасения, что код будут править люди без доступа к IDE, то мы можем (на C#) подчеркнуть суть происходящего значительно менее болезненным способом:
g.drawRect(x1=x1, y1=y1, x2=x2, y2=y2); // именованные параметры в действии

Это аналогично вот такому варианту твоего предложения:
g.drawRect(new Rect{X1=x1, Y1=y1, X2=x2, Y2=y2}; // object initializers спешат на помощь

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

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

M>Теперь перейдем к математике. Стоит заметить, что выражения вида (a, b) = f() встречаются редко. Обычно идет a = f_a(), b = f_b(). Что-то не помню, чтобы из математических функций возвращалиь "пары и более объектов".

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

M>Вектора — да, но это "объект вектор". Возврат прямоугольника скорее всего выглядел бы как "Пусть прямоугольник R — это .... Функция f находит прямоугольник r1, такой, что ...". Т.е. в математических текстах практически везде мы будем иметь "класс" (или запись с именованными полями).


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

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

M>После всего того, что написал, мне кажется наиболее логичным вариант упорядочивания по первому элементу в списке.

Мне кажется наиболее логичным дать в языке средства явного выражения намерений автора.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Использование tuple
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 13.10.09 14:22
Оценка:
Здравствуйте, dr.Chaos, Вы писали:

DC>В Хаскеле есть newtype. Если его применить совместно с туплами перепутать будет невозможно.


Да, но использовать неудобнее — в паттерне надо будет явно выставлять конструктор.
Re[9]: Использование tuple
От: FR  
Дата: 13.10.09 16:40
Оценка: +1 :)
Здравствуйте, maxkar, Вы писали:

VD>>На самом деле программист обязан знать, что делает функция или метод, так как они являются абстракцией. Вопрос в том, что какие-то функции могут получить интуитивные имена, а какие-то нет (не будем же мы давать функциям имена состоящие из 40 слов?). И это не зависит от того используются ли тюплы или нет. Ты точно так же поцдешь смотреть, что делает функция если она возвратит одно значение. Да что там одно? Даже если она вообще ничего не возвращает, ты можешь "пойти". Ну, а вопрос интуитивности это совершенно отдельный вопрос. Для меня Filter и Partition совершенно интуитивно понятные названия определяющие как параметры, так и вовращаемые значения. Если тебе они не ясны, ну, что что или запомнишь, или будешь каждый раз читать документацию.


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


Те же Filter и Partition это практически базовые стандартные ФВП. Их незнание для императивного программиста равносильно например незнанию цикла while.

M>Для библиотек еще можно такого требовать, но для кода организации — нет.


Это даже более базово чем библиотеки, по фундаментальности это что-то среднее между циклами и скажем STL в С++.
Плюс свои функции обычно строятся по образу и подобию базовых ФВП, поэтому их понятность на высоком уровне.
Re[12]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 16:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

VD>>На самом деле ref и out попросту не удобны. Для них нужно объявлять отдельные переменные, да и вообще не красиво. Такие феньки как вложение вызовов функций уже не прокатывают. Плюс есть еще одна проблема. ref и out-параметры требуют, чтобы выражения были lvalue, так что даже если тебе возвращаемое значение не интересно ты не сможешь в ref-параметр передать значение фунции (без промежуточной переменной).


Ага. Это особенно достает, так как приходится скатываться на конкретику там где в этом нет нужды.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 17:04
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Я бы тут постарался развернуть вызов задом наперёд. Так, чтобы code completion мне помогал. Что-то типа

S>
S>GetPersonInfo(123) => (name, _, age);
S>


Это у тебя предрссудок. Видимо он вызван роскознями тех кто придумал from в LINQ спереди ставить.

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

Например, в интеграции немерла ты можешь подвести курсор к имени переменной в паттерне декомпозиции кортежа и увидить ее тип. Были бы имена, то увидил бы и их. Можно даже было бы сделать аналог метод-хинта. Ну, и конечно можно было бы просто генерировать такие переменные по некоторому паттерну. Например, вбиваешь:
def () = GetPersonInfo(123)

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

Ну, и естественно при этом можно было бы и через точку работать. Но об этом я уже говорил.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 17:11
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Тут в соседней ветке Sinclar привел хороший пример с прямоугольниками, так что могу утверждать: даже при чтении (проверке!) вызова функции в виде f(a, b) могут быть проблемы.


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

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

Скажем вместо f(a, b) можно же писать f(arg1 = a, arg2 = b). Но многие будут даже против такой записи (хотя она и правда более информативна). Так что тут компромис. Проблема может быть, но она редка и не страшна. Так что многие предпочитают более краткий синтаксис.

M>Касается как раз тех самых прямоугольников. Например, g.drawRect(x1, y1, x2, y2) является местом потенциальной ошибки (опять же, нужно знать библиотеку или читать документацию). По сравнению с ним вызов g.drawRect(rect) гораздо читабельнее. Да, там нужно проверять, что rect инициализирован правильно, но он то инициализируется через rect.setWidth(width) или rect.setX2(x2), что читается и проверяется легче (глаз цепляется за rest.setWidth(x2) или rect.setX2(width)).


Ну, и? Каков вывод? Отказываемся от передачи более чем одного параметра в функцию?

M>Где-то в ветке уже вывигалась идея, что такая проблема не существует, если элементы кортежа разных типов. С этим я согласен, при разных типах результата (с разной семантикой) путаницы не возникает (так же, как и при передаче параметров).


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

Может быть поступить более конструктивно, и попробовать это новое, и уже потом составить свое мнение?

M>Теперь перейдем к математике.


Уже не хочется, спасибо. Подумай над моими словами. А то у нас получается спор о вкусе устриц с теми, кто их не ел.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 13.10.09 17:13
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>Зато всегда есть имена принимающих переменных, которые названы так, что все понятно. Этого мало?

S>Конечно мало. Возвращаясь к примеру с вызовом обычного метода — у тебя что, всегда имена фактических аргументов совпадают с именами параметров?

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

S>Если убрать имена параметров из стандартной библиотеки, оставив только _1, _2, _3, то как-то трудновато будет разобраться с тем, что происходит, не так ли?


Кто-то заставляет меня, пользуясь туплами, их убирать из стандартной библиотеки? Странные вы люди, все таки. Вот, смотри — синтаксис нотации типов Эрланга, сигнатура функции.

-spec take_smallest(gb_tree()) -> { Key::term(), Value::term(), gb_tree()}


Вопросы по пониманию — есть? С чем спорим?

G>> Кроме того, это имеет вообще значение только в случае, если возвращаемые значения тупла одного типа. Такое бывает редко, и обычно однозначно трактуется, скажем:

G>>Прямоугольник, или линия — ( x1, y1, x2, y2 ).
S>Отлично. Осталось понять, кто из них горизонтальный, а кто вертикальный. И x2 там в третьем члене или width.

В настоящем дизайне я напишу по другому. С туплами, разумеется — заводить для этой фигни типы — overkill. Я напишу так:

-type point() :: { X::integer(), Y::integer() }.
-type rectangle() :: { Left::point(), Right::point() }.

-spec get_rect( ... ) -> rectangle().


И верну тупл, да не один, а цельных три. Вопросы, что вернет get_rect — есть? Или опять остается что-то понять?

G>>Диапазон времени — ( startTime, endTime ).

S>Или всё-таки startTime, timeSpan?

Да ради бога. Это разные типы. timeSpan — это интервал времени в минутах, диапазон — пара времен. Тайпчекер даст по рукам.

G>>Имена принимающих переменных в строке вызова — забыл, нет? На них смотреть религия не позволяет?

S>А если их перепутали?

А если входные аргументы функции перепутали? Что тогда? Будешь из страха, что перепутаешь — по структуре на каждую функцию заводить? Странно, и почему этого никто не боится, а?
Re[10]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 17:19
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Это аналогично вот такому варианту твоего предложения:

S>
S>g.drawRect(new Rect{X1=x1, Y1=y1, X2=x2, Y2=y2}; // object initializers спешат на помощь
S>

S>Заметь, что мы по прежнему опираемся на именование параметров. Даже если мы перепутаем порядок, компилятор восстановит смысл за нас.

Кстити, в немерле 1.0 и C# 4.0 таки есть те самые именнованные параметры. От чего идея именованных возвращаемых параметров становится еще более очевидной, на мой згляд.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 17:24
Оценка:
Здравствуйте, maxkar, Вы писали:

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


Да, ну?

Далее раговаривать нам с тобой не о чем. У нас диаметрально противоположенная база.

M>Для библиотек еще можно такого требовать, но для кода организации — нет.


Я не знаю кто такая "кода организации". Но знаю точно, что минимальная еденица абстракции в программировании на любом языке окромя ассемблера — это функция (метод/процедура). Оспоривать это глупо.

M> Иначе ценность программиста в компании сильно определяется тем, что же из ее кода он знает.


Причем тут код? Надо чеотко понимать, что знать что делает функция, и знать как она реализована — это не одно и то же.
Я не даром говорил об абстракции.

M>А это плохо и для организации, и для самого программиста. Для организации плохо тем, что менять программистов тяжело. Для программиста тем, что полученная информация совершенно не конвертируемая (в другой организации придется учить другой набор велосипедов). Чем меньше нужно учить — тем лучше.


А. Это теория по которой 1000 обезьян должны написать "Войну и мир"? Я с ней не согласен.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 13.10.09 19:18
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Могу. Но разобраться будте проще. Так как мне будте достаточно поглядеть на хинт в IDE и увидить несоответствие.


Нет, не проще. Мне _сейчас_ точно так же вполне достаточно поглядеть на сигнатуру функции, и увидеть несоответствие. И не имеет никакого значения, делаю я это в IDE или нет. Примеры мои видел? Прежде чем писать, посмотри как оно уже выглядит и работает в Эрланге. Это — рабочие примеры. Типы в статике проверяются, точно так же как в С#, не переживай, никакой разницы нет. Статическая проверка типов никак не противоречит динамической типизации.

Кроме того, ошибка эта, о которой ты говоришь — неуловимый Джо. Не помню, чтобы хоть раз путал аргументы функции.

G>>Разница — в чем? Вы о чем вообще говорите, люди? Бред какой-то.


VD>Мы говорим о статически типизированных языках имющих офигительную поддержку IDE. Она в сочетании с языковыми решениями позволяет делать меньше ошибок. Тюплы тут несколько не вписываются в картину.


Они прекрасно вписываются в картину.
Re[6]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 13.10.09 19:21
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

VD>>Он имел в виду, что функцию ты описываешь один раз и если накосячил с именами параметров, то только в ней. А вызваешь ты ее 100 раз и накосячить с именами можешь неограниченно количество раз:


ANS>Именно это я и хотел сказать. Спасибо.


О как. А я, вот, прикинь, сказал ровно то, что хотел.
Re[7]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 13.10.09 19:47
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>> Кроме того, это имеет вообще значение только в случае, если возвращаемые значения тупла одного типа. Такое бывает редко, и обычно однозначно трактуется, скажем:

G>>Прямоугольник, или линия — ( x1, y1, x2, y2 ).
S>Отлично. Осталось понять, кто из них горизонтальный, а кто вертикальный. И x2 там в третьем члене или width.

Как у вас все-таки все на ровном месте сложно и непросто. Не знаете, x вертикальный, или y, и норовите перепутать координаты с width. Для начала — последнее пишется не так. Он вот так пишется.

draw_rect( x1, y1, l, h ).

А первое —

draw_rect( x1, y1, x2, y2 ).

Типичный вызов из графической либы образца 90-х. Туплов в нем нет. Предлагаю поспорить со мной о том, что вызовы функций — это очень опасно. Желательно — с таким же огоньком, с которым вы рассуждаете о туплах .
Re[8]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 13.10.09 20:37
Оценка:
Здравствуйте, Gaperton, Вы писали:

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


VD>>Могу. Но разобраться будте проще. Так как мне будте достаточно поглядеть на хинт в IDE и увидить несоответствие.


G>Нет, не проще. Мне _сейчас_ точно так же вполне достаточно поглядеть на сигнатуру функции, и увидеть несоответствие.


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

G> И не имеет никакого значения, делаю я это в IDE или нет. Примеры мои видел?


Какие?

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


Ну, покажи. Только с объяснениями. А что в Эрлаг уже появились аннотации типов?

G>Это — рабочие примеры. Типы в статике проверяются, точно так же как в С#, не переживай, никакой разницы нет. Статическая проверка типов никак не противоречит динамической типизации.


Всегда был другого мнения.

G>Кроме того, ошибка эта, о которой ты говоришь — неуловимый Джо. Не помню, чтобы хоть раз путал аргументы функции.


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

G>>>Разница — в чем? Вы о чем вообще говорите, люди? Бред какой-то.


VD>>Мы говорим о статически типизированных языках имющих офигительную поддержку IDE. Она в сочетании с языковыми решениями позволяет делать меньше ошибок. Тюплы тут несколько не вписываются в картину.


G>Они прекрасно вписываются в картину.


Да, кто бы спорил.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 03:24
Оценка: +1
Здравствуйте, VladD2, Вы писали:
VD>Это у тебя предрссудок. Видимо он вызван роскознями тех кто придумал from в LINQ спереди ставить.
Ну да, совершенно верно.

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

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

VD>Например, в интеграции немерла ты можешь подвести курсор к имени переменной в паттерне декомпозиции кортежа и увидить ее тип. Были бы имена, то увидил бы и их. Можно даже было бы сделать аналог метод-хинта. Ну, и конечно можно было бы просто генерировать такие переменные по некоторому паттерну. Например, вбиваешь:

VD>
VD>def () = GetPersonInfo(123)
VD>

VD>вызваешь комлейшон внутри скобок или жмешь некоторую комбинацию кнопок и получаешь заполненный список переменных.
Ну, то есть надо прыгать взад-вперёд? Как-то не очень удобно.
VD>Ну, и естественно при этом можно было бы и через точку работать. Но об этом я уже говорил.
Ну, через точку то и так понятно. Вопрос только в присванивании результата в локальную переменную/поле/еще что-то.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.10.09 03:24
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>А мне достаточно. Возвращаясь к примеру — нет, у меня имена параметров всегда настолько информативны, что понятно, что в них лежит. Что и тебе советую. Это совсем не сложно — давать локальным переменным осмысленные имена.

Так, еще раз поясни — ты сейчас про имена параметров или про имена переменных?

G>Вопросы по пониманию — есть? С чем спорим?

В этой сигнатуре компоненты тупла именованные. С чем спорим?


G>В настоящем дизайне я напишу по другому. С туплами, разумеется — заводить для этой фигни типы — overkill. Я напишу так:


G>
G>-type point() :: { X::integer(), Y::integer() }.
G>-type rectangle() :: { Left::point(), Right::point() }.

G>-spec get_rect( ... ) -> rectangle().
G>

G>И верну тупл, да не один, а цельных три. Вопросы, что вернет get_rect — есть? Или опять остается что-то понять?
Опять у туплов компоненты именованные. Конечно, в таком варианте вопросов не будет. Но ты же вроде как утверждал, что туплам достаточно анонимных компонентов. Вот у тебя и будет
-type point() :: { integer(), integer() }.
-type rectangle() :: { point(), point() }.

-spec get_rect( ... ) -> rectangle().


G>А если входные аргументы функции перепутали?

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

G>Типичный вызов из графической либы образца 90-х. Туплов в нем нет.

Зато есть имена у параметров. Нажал Ctrl-space — увидел: "int Height".
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 14.10.09 10:28
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>О как. А я, вот, прикинь, сказал ровно то, что хотел.


Бывает

Замечу, что с передачей параметров в ST нет проблем с именами
Автор: Andrei N.Sobchuck
Дата: 25.09.06
.
А для возврата множества значений предпочитают continuation-passing style
Автор: Andrei N.Sobchuck
Дата: 04.09.06
.

Ухожу-ухожу
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[8]: Использование tuple
От: Курилка Россия http://kirya.narod.ru/
Дата: 14.10.09 10:42
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Замечу, что с передачей параметров в ST нет проблем с именами
Автор: Andrei N.Sobchuck
Дата: 25.09.06
.

ANS>А для возврата множества значений предпочитают continuation-passing style
Автор: Andrei N.Sobchuck
Дата: 04.09.06
.

Это обработка "ошибочной" ситуации, она не покрывает все варианты использования туплов.
Re[9]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 14.10.09 11:26
Оценка: 1 (1)
Здравствуйте, Курилка, Вы писали:

ANS>>А для возврата множества значений предпочитают continuation-passing style
Автор: Andrei N.Sobchuck
Дата: 04.09.06
.

К>Это обработка "ошибочной" ситуации, она не покрывает все варианты использования туплов.

Во-первых, если ты хочешь синтаксиса один-в-один, то, естественно, получатся туплы. Но такая позиция в дискуссии — это тупик.
Во-вторых, деструкция туплов
Автор: Andrei N.Sobchuck
Дата: 23.02.06
a-la int*String (где-то в примере выше я такой видел) один-в-один переводится в код навроде:
people processAllNames: [:index :name |
   '... Тут мы используем индекс и имя ...'
]

Т.е. функция `processAllNames` не возвращает туплу, а передаёт сразу разделённые (типа отматченные) параметры в продолжение.

Ну и, это пример не обработки ошибок, а штатных ситуаций
Автор: Andrei N.Sobchuck
Дата: 05.09.06
.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[10]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.10.09 18:42
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну, то есть надо прыгать взад-вперёд? Как-то не очень удобно.


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

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

S>Ну, через точку то и так понятно. Вопрос только в присванивании результата в локальную переменную/поле/еще что-то.

Ну, дык присвоил локальной переменной, а от нее через точку.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 15.10.09 08:41
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>
ANS>people processAllNames: [:index :name |
ANS>   '... Тут мы используем индекс и имя ...'
ANS>]

ANS>Т.е. функция `processAllNames` не возвращает туплу, а передаёт сразу разделённые (типа отматченные) параметры в продолжение.

Ну, и добавлю, что такие техники в java/c# применять затруднительно из-за отсутствия нелокальных возвратов
Автор: Andrei N.Sobchuck
Дата: 05.09.06
.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[10]: Использование tuple
От: Undying Россия  
Дата: 15.10.09 08:47
Оценка: 1 (1)
Здравствуйте, FR, Вы писали:

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


FR>Те же Filter и Partition это практически базовые стандартные ФВП. Их незнание для императивного программиста равносильно например незнанию цикла while.


Т.е. ни для чего кроме базовых стандартов ФВП (вроде Filter и Partition) туплы использовать нельзя? А зачем они тогда вообще нужны?
Re[11]: Использование tuple
От: nikov США http://www.linkedin.com/in/nikov
Дата: 15.10.09 09:01
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>Ну, и добавлю, что такие техники в java/c# применять затруднительно из-за отсутствия нелокальных возвратов
Автор: Andrei N.Sobchuck
Дата: 05.09.06
.


А в Scala они есть.
Re[12]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 15.10.09 09:39
Оценка:
Здравствуйте, nikov, Вы писали:

ANS>>Ну, и добавлю, что такие техники в java/c# применять затруднительно из-за отсутствия нелокальных возвратов
Автор: Andrei N.Sobchuck
Дата: 05.09.06
.


N>А в Scala они есть.


Я потому и указал java/c#, что на нижележащих платформах их можно эмулировать. Обычно это делается через исключения. Вот я быстренько нашел что так для Scala и есть.
Есть у такого решения и минусы. Первый минус — нефункциональный — скорость работы. Второй минус — функциональный — исключение можно перехватить штатным обработчиком. Я с таким сталкивался когда использовал в ST реализацию на исключениях динамических переменных (dynamic scope). Точнее использовал не я, а сторонняя либа. Один неосторожный обработчик исключения и бабах.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[13]: Использование tuple
От: nikov США http://www.linkedin.com/in/nikov
Дата: 15.10.09 09:46
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>Обычно это делается через исключения. Вот я быстренько нашел что так для Scala и есть.

ANS>Есть у такого решения и минусы. Первый минус — нефункциональный — скорость работы. Второй минус — функциональный — исключение можно перехватить штатным обработчиком. Я с таким сталкивался когда использовал в ST реализацию на исключениях динамических переменных (dynamic scope). Точнее использовал не я, а сторонняя либа. Один неосторожный обработчик исключения и бабах.

Да, есть такая проблема. Я даже как-то открывал ticket по этому поводу. Кажется, в 2.8beta появились более совершенные механизмы (через continuations).
Re[14]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 15.10.09 10:21
Оценка:
Здравствуйте, nikov, Вы писали:

N>Да, есть такая проблема. Я даже как-то открывал ticket по этому поводу. Кажется, в 2.8beta появились более совершенные механизмы (через continuations).


Хм. Так в jvm нет continuations. Или оно как то хитро код преобразует? Но тогда /возможно/ потеряется интероперабельность с java.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[15]: Использование tuple
От: nikov США http://www.linkedin.com/in/nikov
Дата: 15.10.09 10:28
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>Хм. Так в jvm нет continuations. Или оно как то хитро код преобразует?


Да, хитро преобразует. Но я пока не вникал во внутренюю механику.
Причем эти continuations сериализуемые, что позволяет переносить вычисления с одной машины на другую.
Re[11]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.10.09 11:59
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Ты пишешь код один раз. А читаешь много, много раз. Так что при выборе синтаксиса нужно исходить в первую очередь из соображений простоты чтения и привычности для окружающих (принцип — не удивляй).
Ну, при чтении совершенно неважно, объявлены ли имена полей у результата функции. Кроме случая явного именования типа
def (a = x, b = y) = GetSomePoint(c, d);


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

Ну, это как раз boilerplate.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Использование tuple
От: _FRED_ Черногория
Дата: 15.10.09 12:15
Оценка:
Здравствуйте, Undying, Вы писали:

U>Чем:

U>interface ITest
U>{
U>  Tuple<string FirstName, string LastName> Name { get; }
U>}

U>хуже, чем
U>class Name
U>{
U>  string FirstName;
U>  string LastName;
U>}

U>inteface ITest
U>{
U>  Name Name { get; }
U>}

U>Первый случай как бы не более понятен…

Лучше тем, что, во-первых, позволяет добавить документацию\метаинформацию(аттрибуты) и т.д. Во-вторых, позволяет выбрать, значимый или ссылочный тип отдаётся наружу. В-третьих, позволяет расширять (без _изменения_ самого типа! только _расширением_ его) функциональность возвращаемого значения: добавлять события, реализацию интерфейсов и т.п.

ИМХО, в бизнес-сущностях несомненно следует не лениться и не экономить на спичках и выписать (читай: явно обозначить) используемый тип. Делать же это (специальный тип) для "приватных" расчётов — там, где не требуется поддержка совместимости, документация, соблюдение протокола — таплы самое оно. всё, что им не хватает в шарпе: синтаксического сахара — если не ударяться в маразм (с использованием восмиарных таплов) всё будет прекрасно.

Именованные записи способны ещё более улучшить кейс использования таплов _в приватных частях_, но не надо стремиться заместить ими бизнес-сущности: сидеть на двух табуретах будет очень не комфортно.

U>(как, к примеру, Func<T, T, int> на практике оказывается понятнее Comparison<T>) и при этом кода требует в разы меньше.


Не согласен: ИМХО наоборот, использование Comparison, Predicate и прочего подобного удобнее и _очевиднее_ в использовании.
Help will always be given at Hogwarts to those who ask for it.
Re[12]: Использование tuple
От: _FRED_ Черногория
Дата: 15.10.09 12:45
Оценка:
Здравствуйте, _FRED_, Вы писали:

U>>Чем:


U>>хуже, чем

U>>Первый случай как бы не более понятен…

_FR>Лучше тем, что, во-первых, …


Имелось в виду, что второй способ с явным объявлением типа лучше (по моему мнению).
Help will always be given at Hogwarts to those who ask for it.
Re[11]: Использование tuple
От: FR  
Дата: 15.10.09 13:41
Оценка:
Здравствуйте, Undying, Вы писали:

U>Т.е. ни для чего кроме базовых стандартов ФВП (вроде Filter и Partition) туплы использовать нельзя? А зачем они тогда вообще нужны?


Странный вывод

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

Кроме того кортежи весьма интенсивно используются в вариантных типах данных, например:

type 'a btree = Empty | Node of 'a * 'a btree * 'a btree;;
Re[12]: Использование tuple
От: vdimas Россия  
Дата: 15.10.09 20:50
Оценка: 2 (1) +1
Здравствуйте, IT, Вы писали:


IT>В параметрах лябды тоже тушим свет? Да и вообще при вызове любого метода при передаче параметров легко запутаться. Ага.


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

Например (как бы это могло в C# выглядеть):
class SomeMap<TKey, TValue> {
    public {TKey key, TValue value}[] ToArray() {...}
}

foreach(var pair in someMap.ToArray()) 
    Console.WriteLine("{0}:{1}", pair.key, pair.value);


Пусть имена полей тупла "key" и "value" существуют только как метаинформация и используются компилятором как синонимы. Без расширения системы метаинформации .Net это можно было сделать на аттрибутах, чтобы реально компилировалось в нечто вроде:

class SomeMap<TKey, TValue> {
  [return: TupleField(0, "key"), return:TupleField(1, "value")]
    public Tuple<TKey, TValue>[] ToArray() {...}
}

foreach(Tuple<TKey, TValue> pair in someMap.ToArray()) 
    Console.WriteLine("{0}:{1}", pair._0, pair._1);
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[9]: Использование tuple
От: vdimas Россия  
Дата: 15.10.09 21:09
Оценка:
Здравствуйте, Mystic, Вы писали:


M>Это зависит от предметной области. В MATLAB таких функций много.


Насколько я помню, в матлабе кортежи используются только для возвращаемых значений ф-ий и подачи их как параметров. И самое главное: поля этого кортежа имеют вполне осмысленные имена, а не безликие типы (ибо тип данных у матлаба всего один — матрица).
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[8]: Использование tuple
От: vdimas Россия  
Дата: 15.10.09 21:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Я бы тут постарался развернуть вызов задом наперёд. Так, чтобы code completion мне помогал. Что-то типа

S>
S>GetPersonInfo(123) => (name, _, age);
S>


Да, не первый раз это высказывается и сновной аргумент "против" — сам вызов с длинным перечнем параметров отодвинет далеко по тексту результат, а внимание обычно надо фокусировать именно на результате. Хотя, для коротких вызовов, типа приведенного — очень даже ничего.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[12]: Использование tuple
От: Undying Россия  
Дата: 16.10.09 06:18
Оценка: 1 (1)
Здравствуйте, FR, Вы писали:

U>>Т.е. ни для чего кроме базовых стандартов ФВП (вроде Filter и Partition) туплы использовать нельзя? А зачем они тогда вообще нужны?


FR>Странный вывод


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


И к чему тогда было твое замечание про Filter и Partition? У этих функций не контекст ясен, а знание механизма их работы вбито в головы функциональщиков. Но на практике даже при работе со своим кодом механизм работы большинства функций забывается очень быстро, в случае же с чужим кодом механизм работы зачастую просто неизвестен.

maxkar же говорил о том, что даже при правильно поставленной задаче функции и удачно подобранном названии контекст ясен далеко не всегда и допускает неоднозначность возвращаемого значения. Именованные возвращаемые значения позволяет эту неоднозначность снизить очень значительно, неименнованные туплы неоднозначность сохраняют.
Re[13]: Использование tuple
От: FR  
Дата: 16.10.09 15:20
Оценка: +2
Здравствуйте, Undying, Вы писали:


U>И к чему тогда было твое замечание про Filter и Partition? У этих функций не контекст ясен, а знание механизма их работы вбито в головы функциональщиков. Но на практике даже при работе со своим кодом механизм работы большинства функций забывается очень быстро, в случае же с чужим кодом механизм работы зачастую просто неизвестен.


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

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


Да для языка без паттерн матчинга он совершенно прав. Для языков с паттерн матчингом, необходимости в именах я не вижу.
Re[13]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.10.09 19:23
Оценка: :)
Здравствуйте, Undying, Вы писали:

U>И к чему тогда было твое замечание про Filter и Partition? У этих функций не контекст ясен, а знание механизма их работы вбито в головы функциональщиков. Но на практике даже при работе со своим кодом механизм работы большинства функций забывается очень быстро, в случае же с чужим кодом механизм работы зачастую просто неизвестен.



Скажи, ты хоть раз в жизни забыл, что i в for (int i = 0; ...) — это индексер цикла?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Использование tuple
От: maxkar  
Дата: 18.10.09 17:54
Оценка:
Здравствуйте, VladD2, Вы писали:

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


Ну вот в двух первых сообщениях этой подветки Рысцов Денис и IDL поделились своим опытом. Каким-либо основаниям считать их опыт неподходящим указано не было. Причем Денис указал даже срок — 1 год, что характеризует класс проекта. Затем вы обвинили их в "не внятности ваших функций". Только вот дальше оказалось, что пример в том обвинении тоже "не очень". Сама по себе "в вакууме" приведенная функция оказалась тоже не слишком внятной. Пришлось применять аргументы вроде "это везде в ФП" и подобные, чтобы объяснить, почему же результат именно такой, как есть. Только вот аргумент этот не слишком универсальный. Не под все проекты подходит. Не всегда можно выделить четкую и неизменную предметную область, в которой ничего не меняется. Например, курсовая вполне может служить примером проекта с не слишком четкими требованиями. Вполне вероятно, какие-то требования менялись в процессе работы (в зависимости от полученных результатов, требований научного руководителя и т.п.). Там как раз можно наблюдать много эффектов "промышленного программирования" вроде необходимости много читать старый код и что-то менять. Их вывод был — tuples (неименнованные) в публичных интерфейсах на их проектах доставляют больше неудобств, чем дают преимуществ.

VD>Скажем вместо f(a, b) можно же писать f(arg1 = a, arg2 = b). Но многие будут даже против такой записи (хотя она и правда более информативна). Так что тут компромис. Проблема может быть, но она редка и не страшна. Так что многие предпочитают более краткий синтаксис.


Можно писать и длинно (с именованием). Можно писать коротко. С тем, что компромиссы нужны, согласен. В идеале нужно иметь возможность писать и так и так. Например, в ocaml'евском биндинге для gtk (lablgtk называется) большинство примеров, которые я видел, было с передачей именованных параметров. И в тех конкретных случаях оно повышало читаемость (много функций с опциональными аргументами). Большинство же примеров по caml'у да и программ, которые я писал на нем, используют неименованные (позиционные) аргументы. Что еще интересно — там же, в стандартной библиотеке, присутствуют наборы функций с именованными параметрами, являющимися аналогами функций без именованных парамтеров. Например, ArrayLabels, ListLabels, StdLabels, MoreLabels. В тех же ArrayLabels есть и функции с "несколькими" параметрами, которые не имеют именованных аргументов. Но практически во всех этих функциях параметры разных типов. В остальных обычно именована пара интов (позиция и длина), хотя есть и blit, в котором поименована также и пара массивов. Я не думаю, что такие функции появились "просто так", видимо, в определенных случаях они были нужны (хотя я их не использовал).

VD>Ну, и? Каков вывод? Отказываемся от передачи более чем одного параметра в функцию?


Да нет, компромиссы нужны... Хотя есть языки, где фактически все функции и являются функциями от одного аргумента. Как раз таки эти функции обычно в ФП и встречаются (хотя называется это вроде бы curring).

M>>Где-то в ветке уже вывигалась идея, что такая проблема не существует, если элементы кортежа разных типов. С этим я согласен, при разных типах результата (с разной семантикой) путаницы не возникает (так же, как и при передаче параметров).


VD>А тебе не кажется, что ты обсасываешь не стоящую выеденного яйца, гипотетическую проблему теолько для того, чтобы не принимать ничего нового?


Нет, не кажется. Мне кажется, что для ряда проектов (с определенными характеристиками) эта проблема является далеко не гипотетической и стоит разбираться, в каких же случаях на нее стоит обращать внимание и как ее решать. Я же нигде не говорил, что кортежи вообще нельзя использовать, а всего лишь утверждал, что они в ряде случаев могут доставлять проблемы. В дальнейшем потенциальные проблемы с описанием причинм могут быть сформулированы в виде coding guidelines для начинающих программистов, например. Ну вот напишут junior'ы код и получат те же проблемы, что уже получили другие люди до них. А так глядишь — и задумаются.

VD>Может быть поступить более конструктивно, и попробовать это новое, и уже потом составить свое мнение?


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

M>>Теперь перейдем к математике.


VD>Уже не хочется, спасибо. Подумай над моими словами. А то у нас получается спор о вкусе устриц с теми, кто их не ел.

Да подумал вроде. Только вот вы IDL'у (и Денису) то возражали слишком категорично. Однозначных аргументов за кокнретные варианты там не было. Были отсылки к "традициями ФП" и т.п., которые очень плохо приводятся к "предметной области", где ситуация может быть совсем другая. Так что мне кажется, что вы просто не видели тех проектов и команд, на которых их рекомендации могут быть оправданы.
Re[14]: Использование tuple
От: maxkar  
Дата: 18.10.09 19:17
Оценка: +1
Здравствуйте, FR, Вы писали:

FR>К тому что судя по всему люди не понимали что это базовые вещи.


Не про меня, случайно? Я прекрасно понимаю, что это базовые вещи. И partition использовался потому, что был приведен примером в том сообщении, с которым я не согласился. Не видел особого смысла менять пример. Если же восстановить контекст того сообщения, на которое я в начале отвечал, получается, что в нем была аргументация в духе "вы не умеете писать внятные функции, вот чем не очевидна partition". И в результате получили, что "partition является одной из базовых функций ФП, и поэтому ее значение нужно знать". С последним утверждением я никогда не спорил. Но если использовать только его для аргументации исходного сообщения, мы получим "вы не умеете писать внятные функции, потому что есть вот базовая функция partition, по которой совершенно очевидно, что она делает". Слово "базовая" — ключевое. Без него все обвинения в "незнаинии ФП" не работают. А с ним не работает пример. Точнее, он "условно" работает — только в определенном контексте. Доказательств же того, что оно работает всегда я не увидел (хотя вроде бы пытался получить), утверждение же Влада было сформулировано в категорической форме (для вообще всех функций, не только базовых).
FR>Очень многие чужие и свои функции делаются по образу и подобию базовых, это хороший стиль.
Это если есть базовые функции. А если их нет? Или программист их не знает (бывает такое у junior'ов)? Последнее, правда, решается не средствами языка.
Еще более интересная ситуация, когда "базовых" вещей не одна. Вот совершенно недавний пример. Разбор SWF-файла. Вот в нем есть структура "прямоугольник". И кроме выборов для tuple для их хранения есть не только (x1, y1, x2, y2) и (x1, y1, width, height). В этих двух случаях можно было бы взять то, что используется в gui (том же gtk, например). Проблема в том, что в "предметной области" (SWF-файл) там вообще (x1, x2, y1, y2). И из-за этого отдать предпочтение ни одному из вариантов нельзя. Если взять базовый формат SWF нужно следить за новыми программистами и правильностью понимания прямоугольника. Если же взять понимание GUI (с шириной или второй координатой), то прямоугольник может неправильно трактовать программист, знающий формат файла. Это все решается guidelines не в коде, ревью и т.п. Если же вернуться к примеру, сформулируем так: "разве не очевидно, что возвращает функция"

readRectangle :: IO (Int, Int, Int, Int)
readRectangle = ...


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

Ну, если хочется, можно назвать readSWFRectangle, сильно это не поможет (ну читает оно прямоугольник, а вот формат хранения или формат возвращаемых результатов закодирован в SWF — не понятно). Можно назвать readSWFRectangleInSWFFormat, но это уже длинно. Я выбрал "именованные поля" в типе Rectangle. Это устраняет указанные неоднозначности.

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

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


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


Ну вот чуть выше был SWF'овский прямоугольник. А так я верю, что вам имена и были не нужны. Возможно, на это влиял стиль программирования и т.п. (ничего плохого не имею в виду) Но вот в Haskell (с pattern match'ингом) были введены таки именованные поля для алгебраических типов. Объявление выглядит примерно так:
data SomeData =
         SomeData {
             int1 :: Int,
             int2 :: Int
         }

И генерирует функции int1 :: SomeData -> Int и int2 :: SomeData -> Int. При правильном экспорте функций из модуля оно позволяет и внутреннюю структуру менять без изменения пользователей , например, еще поле добавить (что было бы более трудоемко в случае pattern-матчинга). Кстати, возможно именно такая возможность изменять структуру была причиной введения "именованных полей", а не читаемость. Ну тогда мы получиим почти утверждение Дениса "стараться не использовать кортежи в публичных функциях", но на случай функций, у которых возвращаемое значение еще не устоялось.
Re[10]: Использование tuple
От: maxkar  
Дата: 18.10.09 20:15
Оценка: +1
Здравствуйте, VladD2, Вы писали:

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


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


Ну совсем не знать — не может. Не знать какие-то детали (граничные случаи и т.п.) — вполне. И вполне может не знать каких-то функций модуля, методов класса и т.п. Такое незнание, кстати, достаточно часто встречается. Вот и с порядком аргументов иногда может путаться (если нечасто функцию использует).

VD>Да, ну?


VD>Далее раговаривать нам с тобой не о чем. У нас диаметрально противоположенная база.


Ну у меня не то, чтобы такая незыблемая база. На самом деле очень хорошо работать с программистом, который отлично знает все API, которое используется. Только это, во-первых, долго (база самописного кода в организации может быть большой), а во-вторых, далеко не всем может быть интересно. Есть программисты, которым достаточно сделать "чтобы работало", которые не изучают используемые классы, например, а пишут код на базе примеров из интернета и существующего кода, а также ищут методы для использования через подсказку в IDE. И это тоже существующая реальность. Ее не только я наблюдал, например в "Practical API Design. Confession of a Java Framework Architect" автор тоже пишет, что есть такие программисты (ну и еще развивает мысль clueless programming, когда не нужно тратить много времени на обучение, чтобы выполнять задачи). Сама по себе ориентация на программиста, не читающего документацию, имеет свои преимущества — его можно на 2-3 день сажать решать реальные задачи. Если захочет — выучит API подробнее, не захочет — вомзожно, найдет более интересную подсистему и т.п. Да, такое не везде применимо, нужно смотреть конкретные случаи.

M>>Для библиотек еще можно такого требовать, но для кода организации — нет.


VD>Я не знаю кто такая "кода организации". Но знаю точно, что минимальная еденица абстракции в программировании на любом языке окромя ассемблера — это функция (метод/процедура). Оспоривать это глупо.


"Код организации" — это то, что написали и не удалили программисты организации за время существования этой организации (плюс купленный код и т.п.). В небольшом проекте на 200-300 классов и порядка 5 методов/полей на класс мы получим 1000 "единиц абстракции". Одному человеку их знать тяжело (а если проект большой, то получаем вообще огромную проблему), абстракции забываются. Уровни абстракций спасают (это если их не забыли на определенном этапе ввести, иначе получится спагетти на те же 1000 "произвольно связанных" абстракций, что гораздо хуже). Но при подходе "сначала изучи систему, затем что-то меняй", у архитекторов/тим-лидов возникнут проблемы — чтобы залезть куда-то на нижнии уровни абстракций, нужно учить подсистему (или сразу все знать). В моем же подходе — может. Да, это будет медленнее, чем у ответственного за соответствующую подсистему, но все равно возможно. Ему не нужно досконально знать, что делают 10 функций, вызываемых из другой (при поисках багов детали реализации все равно приходится читать). С большой вероятностью он не зная деталей все равно найдет вероятное место ошибки (хотя может и не повезти). Обычно то он туда лазить не должен, но проекты (бюджет, количество программистов и их квалификация) бывают разные. На некоторых проектах подобная необходимость бывает оправданной.

M>> Иначе ценность программиста в компании сильно определяется тем, что же из ее кода он знает.


VD>Причем тут код? Надо чеотко понимать, что знать что делает функция, и знать как она реализована — это не одно и то же.

VD>Я не даром говорил об абстракции.

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

M>>А это плохо и для организации, и для самого программиста. Для организации плохо тем, что менять программистов тяжело. Для программиста тем, что полученная информация совершенно не конвертируемая (в другой организации придется учить другой набор велосипедов). Чем меньше нужно учить — тем лучше.


VD>А. Это теория по которой 1000 обезьян должны написать "Войну и мир"? Я с ней не согласен.


Ну на теорию это не тянет, скорее вопрос личных предпочтений. Вышесказанное — личное мнение (даже не мною впервые сформулированно). Я пока не видел работ, в которых исследуется влияние подобных акцентов (акцент на знание систем или ориентация на незнание) на проекты. Мне нравятся проекты, где можно акцентироваться и на возможности "легко и просто" отдать компонент/систему/разработку другим разработчикам. Фактически, снизить риски при необходимости замены программиста другим (не сделать из программистов винтики, не получается, остается много простора для творчества), меня в том числе. При получении же программиста в команду достаточно быстро начать получать от него пользу (вне зависимости от квалификации). Если я правильно понял вашу позицию, вы утверждаете, что перед постановкой программисту задач ему нужно "прочитать лекцию" по подсистеме (дать время на обучение необходимому набору абстракций). Такая позиция тоже имеет право на жизнь. В ряде случаев она вполне может быть идеальной для разработки. Но в большинстве случаев применимы оба подхода, каждый имеет свои достоинства и недостатки и на практике получается их смесь в различных пропорциях. Поэтому "не согласиться" с вашей позицией я тоже не могу. Могу только признать, что нам нравятся разные подходы к разработке. И даже не смотря на это приходится наблюдать за другими подходами — в них бывают интересные идеи, которые можно "утащить себе". Да и может так оказаться, что окажусь в ситуации, когда мне понравится противоположный подход. В этом смысле обсуждения различных достоинств и недостатков различных подходов как раз очень и очень полезны.
Re[15]: Использование tuple
От: FR  
Дата: 19.10.09 12:33
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Не про меня, случайно? Я прекрасно понимаю, что это базовые вещи. И partition использовался потому, что был приведен примером в том сообщении, с которым я не согласился. Не видел особого смысла менять пример. Если же восстановить контекст того сообщения, на которое я в начале отвечал, получается, что в нем была аргументация в духе "вы не умеете писать внятные функции, вот чем не очевидна partition". И в результате получили, что "partition является одной из базовых функций ФП, и поэтому ее значение нужно знать". С последним утверждением я никогда не спорил. Но если использовать только его для аргументации исходного сообщения, мы получим "вы не умеете писать внятные функции, потому что есть вот базовая функция partition, по которой совершенно очевидно, что она делает". Слово "базовая" — ключевое. Без него все обвинения в "незнаинии ФП" не работают. А с ним не работает пример. Точнее, он "условно" работает — только в определенном контексте. Доказательств же того, что оно работает всегда я не увидел (хотя вроде бы пытался получить), утверждение же Влада было сформулировано в категорической форме (для вообще всех функций, не только базовых).


Я никого ни в чем ни обвиняю
И мне лень разбираться с контекстами


M>Это если есть базовые функции. А если их нет? Или программист их не знает (бывает такое у junior'ов)? Последнее, правда, решается не средствами языка.


Где это их нет?
Если не знает пусть учит.

M>Еще более интересная ситуация, когда "базовых" вещей не одна. Вот совершенно недавний пример. Разбор SWF-файла. Вот в нем есть структура "прямоугольник". И кроме выборов для tuple для их хранения есть не только (x1, y1, x2, y2) и (x1, y1, width, height). В этих двух случаях можно было бы взять то, что используется в gui (том же gtk, например). Проблема в том, что в "предметной области" (SWF-файл) там вообще (x1, x2, y1, y2). И из-за этого отдать предпочтение ни одному из вариантов нельзя. Если взять базовый формат SWF нужно следить за новыми программистами и правильностью понимания прямоугольника. Если же взять понимание GUI (с шириной или второй координатой), то прямоугольник может неправильно трактовать программист, знающий формат файла. Это все решается guidelines не в коде, ревью и т.п. Если же вернуться к примеру, сформулируем так: "разве не очевидно, что возвращает функция"


Не используй тут кортежи используй структуры.

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


Но это не повод не знать базу.

M>Ну вот чуть выше был SWF'овский прямоугольник. А так я верю, что вам имена и были не нужны. Возможно, на это влиял стиль программирования и т.п. (ничего плохого не имею в виду) Но вот в Haskell (с pattern match'ингом) были введены таки именованные поля для алгебраических типов. Объявление выглядит примерно так:

M>
M>data SomeData =
M>         SomeData {
M>             int1 :: Int,
M>             int2 :: Int
M>         }
M>


Это не кортеж, а структура (в OCaml и F# есть аналогичный тип — record)
Re[11]: Использование tuple
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.10.09 17:10
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Ну вот в двух первых сообщениях этой подветки Рысцов Денис и IDL поделились своим опытом. Каким-либо основаниям считать их опыт неподходящим указано не было. Причем Денис указал даже срок — 1 год, что характеризует класс проекта. Затем вы обвинили их в "не внятности ваших функций". Только вот дальше оказалось, что пример в том обвинении тоже "не очень". Сама по себе "в вакууме" приведенная функция оказалась тоже не слишком внятной. Пришлось применять аргументы вроде "это везде в ФП" и подобные, чтобы объяснить, почему же результат именно такой, как есть. Только вот аргумент этот не слишком универсальный. Не под все проекты подходит. Не всегда можно выделить четкую и неизменную предметную область, в которой ничего не меняется. Например, курсовая вполне может служить примером проекта с не слишком четкими требованиями. Вполне вероятно, какие-то требования менялись в процессе работы (в зависимости от полученных результатов, требований научного руководителя и т.п.). Там как раз можно наблюдать много эффектов "промышленного программирования" вроде необходимости много читать старый код и что-то менять. Их вывод был — tuples (неименнованные) в публичных интерфейсах на их проектах доставляют больше неудобств, чем дают преимуществ.


Не надо инсинуаций. IDL просто никогда в жизни не использовал кортежи, так что озвучивал всего лишь свои предрассудки.

Рысцов же описывал свою практику. Собственно похожая практика есть у всех приличных программистов.
Слова "стараться не использовать" не означает, что ни в коем случае не использовать. В очевидных случаях их очень даже можно использовать. Ну, и в некоторых случаях удобнее написать коментарий, а не лепить лишние типы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Использование tuple
От: jenyavb  
Дата: 20.10.09 12:57
Оценка:
Здравствуйте, IDL, Вы писали:

IDL>В FW 4 появился новый тип tuple, перенятый с функциональных языков.


Вот интересно, нафига сделали Tuple<T>? Зачем кортеж с одним значением?
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
Re[2]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.10.09 13:41
Оценка: +3
Здравствуйте, jenyavb, Вы писали:

J>Вот интересно, нафига сделали Tuple<T>? Зачем кортеж с одним значением?

Лично меня больше интересует другой вопрос: где Tuple? Т.е. кортеж с нулём значений?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 20.10.09 15:08
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>А мне достаточно. Возвращаясь к примеру — нет, у меня имена параметров всегда настолько информативны, что понятно, что в них лежит. Что и тебе советую. Это совсем не сложно — давать локальным переменным осмысленные имена.

S>Так, еще раз поясни — ты сейчас про имена параметров или про имена переменных?

Сейчас — про имена переменных, а не параметров.

G>>Вопросы по пониманию — есть? С чем спорим?

S>В этой сигнатуре компоненты тупла именованные. С чем спорим?

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

G>>В настоящем дизайне я напишу по другому. С туплами, разумеется — заводить для этой фигни типы — overkill. Я напишу так:


G>>
G>>-type point() :: { X::integer(), Y::integer() }.
G>>-type rectangle() :: { Left::point(), Right::point() }.

G>>-spec get_rect( ... ) -> rectangle().
G>>

G>>И верну тупл, да не один, а цельных три. Вопросы, что вернет get_rect — есть? Или опять остается что-то понять?
S>Опять у туплов компоненты именованные. Конечно, в таком варианте вопросов не будет. Но ты же вроде как утверждал, что туплам достаточно анонимных компонентов. Вот у тебя и будет

Да, именно так — я утверждал что это в большинстве случаев minor issue. Мой пример с именами не отменяет
этого тезиса.

S>
S>-type point() :: { integer(), integer() }.
S>-type rectangle() :: { point(), point() }.

S>-spec get_rect( ... ) -> rectangle().
S>


Не совсем так грустно. В вызывающем коде у меня будет:

{ { X, Y } = LeftPoint, RightPoint } = get_rect( ... )

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

G>>А если входные аргументы функции перепутали?

S>Еще раз поясняю: у входных аргументов, помимо типов, есть имена. Отсюда желание завести имена и у выходных аргументов, и по той же причине.

Достаточно заглянуть в онлайн-документацию к любой функции Эрланга, чтобы понять, насколько это на самом деле критично. Это почти пофигу на практике. По опыту говорю. Хотя — да, приятно, я предпочитаю инлайнить имена в декларациях типов сомнительных случаях.
Re[9]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 20.10.09 15:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>Типичный вызов из графической либы образца 90-х. Туплов в нем нет.

S>Зато есть имена у параметров. Нажал Ctrl-space — увидел: "int Height".

Пишешь это в комментарии выше — никаких проблем.
Re[10]: Использование tuple
От: Sinclair Россия https://github.com/evilguest/
Дата: 21.10.09 04:13
Оценка:
Здравствуйте, Gaperton, Вы писали:
G>Пишешь это в комментарии выше — никаких проблем.
Хм. Имена параметров ты один раз описываешь в декларации функции и потом пользуешься столько раз, сколько раз ты её вызываешь.
Комментарии ты предлагаешь писать при каждом вызове. Или ты имеешь в виду — описывать их в док-комментарии на функцию, который тебе будет показывать IDE?
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 21.10.09 12:34
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

G>>Пишешь это в комментарии выше — никаких проблем.
S>Хм. Имена параметров ты один раз описываешь в декларации функции и потом пользуешься столько раз, сколько раз ты её вызываешь.
S>Комментарии ты предлагаешь писать при каждом вызове. Или ты имеешь в виду — описывать их в док-комментарии на функцию, который тебе будет показывать IDE?

Последнее, конечно. Комментарий к функции хотя бы в пару строк писать все равно надо. Запись

-spec get_point() -> { X::integer(), Y::integer() }
get_point() -> ...

от

get_point() -> %% { X::integer(), Y::integer() } — да что угодно тут писать можно, можно сверху или снизу
...

отличается мало. Данный пример завязан на синтаксис Эрланга, но это неважно.

Ключевой момент в том, что имена элементов тупла функциональной нагрузки не несут, компилятор их игнорирует — это по своей сути все равно комментарий. Потому, что поля задаются на самом деле порядком элементов в контрукторе, — так же, как и при вызове функции. Поэтому, это на самом деле не так важно, допускает синтаксис их писать в объявлении типа, или нет. Почувствовать это можно после некоторой практики употребления туплов. Понять — можно и так. Я тебе дело говорю, Синклер, поверь (и проверь сам — я знаю на слово ты не веришь, и это правильно) — нет тут большой проблемы .
Re[12]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 21.10.09 12:49
Оценка: +1
Здравствуйте, Gaperton, Вы писали:
G>-spec get_point() -> { X::integer(), Y::integer() }
G>get_point() -> ...

G>от


G>get_point() -> %% { X::integer(), Y::integer() } — да что угодно тут писать можно, можно сверху или снизу

G>...

G>отличается мало. Данный пример завязан на синтаксис Эрланга, но это неважно.


Я имею в виду — отличаются в плане возможности автоподсказки, а не контроля типов. Пример на С-подобном псевдокоде, при "хорошем синтаксисе" выглядел бы как-то так:

( int X, int Y ) get_point() { ... }

Вместо

void get_point( int & o_X, int & o_Y ) { ... }

Лучше, не так ли? И не ломает традицию синтаксиса С — возвращаемый тупл должен по возможности выглядеть похоже на аргументы функции, чтобы быть естественным продолжением языка, а не выглядеть уродливо. Сохраняя традицию С, имена X и Y должны быть доступны внутри определения функции, что, кстати, было бы офигенно удобно. Я бы сделал как-то так, добавляя туплы в С-подобный язык. Ну, разрешил бы еще писать просто return в таких случаях, вероятно, а также return с явным туплом. Разумеется, тупл сделал бы l-value.

Или же

( int /* X */, int /* Y */ ) get point() { ... }

но так делать резона нет никакого.
Re[13]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 21.10.09 12:56
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Лучше, не так ли? И не ломает традицию синтаксиса С — возвращаемый тупл должен по возможности выглядеть похоже на аргументы функции, чтобы быть естественным продолжением языка, а не выглядеть уродливо. Сохраняя традицию С, имена X и Y должны быть доступны внутри определения функции, что, кстати, было бы офигенно удобно. Я бы сделал как-то так, добавляя туплы в С-подобный язык. Ну, разрешил бы еще писать просто return в таких случаях, вероятно, а также return с явным туплом. Разумеется, тупл сделал бы l-value.


Это предложение, как ты видишь, отличается от предложения немерлистов своей простотой и минимальным надругательством над базовым языком. И поднятый вопрос об "опасности" туплов при таком подходе, как ты понимаешь — не стоит вовсе, ибо это есть органическое продолжение базового языка. Вот это и называется, ИМХО — добавлять туплы в язык _правильно_.

А то, что они добавлены по факту через задний проход — это уже не проблемы собственно туплов как таковых.
Re[8]: Использование tuple
От: Gaperton http://gaperton.livejournal.com
Дата: 21.10.09 14:36
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>Замечу, что с передачей параметров в ST нет проблем с именами
Автор: Andrei N.Sobchuck
Дата: 25.09.06
.

ANS>А для возврата множества значений предпочитают continuation-passing style
Автор: Andrei N.Sobchuck
Дата: 04.09.06
.


О как. А для группировки аргументов при вызове функции в ST тоже CPS предпочитают? Пример:

call( M, N, O, { empty, 1, 2, 3 } )
...
State = { start, X, Y, Z },
call( M, N, O, State )
...

ANS>Ухожу-ухожу


Да лана, оставайся.
Re[3]: Использование tuple
От: jenyavb  
Дата: 22.10.09 06:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

J>>Вот интересно, нафига сделали Tuple<T>? Зачем кортеж с одним значением?

S>Лично меня больше интересует другой вопрос: где Tuple? Т.е. кортеж с нулём значений?

И зачем же он?
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
Re[9]: Использование tuple
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 22.10.09 11:00
Оценка:
ANS>>Замечу, что с передачей параметров в ST нет проблем с именами
Автор: Andrei N.Sobchuck
Дата: 25.09.06
.

ANS>>А для возврата множества значений предпочитают continuation-passing style
Автор: Andrei N.Sobchuck
Дата: 04.09.06
.

G>О как. А для группировки аргументов при вызове функции в ST тоже CPS предпочитают? Пример:

G>State = { start, X, Y, Z },

G>call( M, N, O, State )
G>...

Внутри себя ВМ манипулирует массивами. Именованные переменные это чисто синтаксический сахар. То есть, можно ВМ попросить сделать деструкцию массива. Так что конкретно этот твой пример можно развернуть через два блока так:
someMethod
"..."

 "теперь делаем вызов с CPS"
 self doSomeCall: [ :M :N :O :State |
  [ :Start :X :Y :Z |
    "тут мы имеем доступ и к M, N, O и к Start, X, Y, Z"
    ^ M * N + X + Y - Z."нелокальный возврат"
  ] valueWithArguments: State.
]


И, заметь, без особого синтаксического оверхеда Я проверил — работает.
Но я не думаю, что так хоть кто-то делает. Просто потому, что не-функциональные программы изначально проектируются по другому.
На любом языке, конечно, можно писать как на Фортране Но зачем? Так что пример, где нужно сделать деструкцию двух массивов, можешь не писать
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[4]: Использование tuple
От: Oksana Gimmel http://oksana-gimmel.blogspot.com/
Дата: 22.10.09 11:18
Оценка:
Здравствуйте, jenyavb, Вы писали:

S>>Лично меня больше интересует другой вопрос: где Tuple? Т.е. кортеж с нулём значений?


J>И зачем же он?


Как нормальная замена void. Чтобы можно было функции, возвращающие значения, и функции, не возвращающие значения, однотипно полиморфно обрабатывать.
asato ma sad gamaya
Re[5]: Использование tuple
От: jenyavb  
Дата: 22.10.09 11:58
Оценка:
Здравствуйте, Oksana Gimmel, Вы писали:

S>>>Лично меня больше интересует другой вопрос: где Tuple? Т.е. кортеж с нулём значений?

J>>И зачем же он?
OG>Как нормальная замена void. Чтобы можно было функции, возвращающие значения, и функции, не возвращающие значения, однотипно полиморфно обрабатывать.

Так для этого в функциональных языках ведь есть специальный тип, вроде (), зачем его с кортежами мешать?
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
Re[6]: Использование tuple
От: Oksana Gimmel http://oksana-gimmel.blogspot.com/
Дата: 22.10.09 12:13
Оценка: 5 (2) +1
Здравствуйте, jenyavb, Вы писали:

OG>>Как нормальная замена void. Чтобы можно было функции, возвращающие значения, и функции, не возвращающие значения, однотипно полиморфно обрабатывать.


J>Так для этого в функциональных языках ведь есть специальный тип, вроде (), зачем его с кортежами мешать?


Ну так это он и есть.
http://en.wikipedia.org/wiki/Unit_type

One may also regard the unit type as the type of 0-tuples, i.e. the product of no types.
...
In the functional programming language Haskell, the unit type is called () and its only value is also (), reflecting the 0-tuple interpretation.

asato ma sad gamaya
Re[5]: Использование tuple
От: Jack128  
Дата: 22.10.09 13:43
Оценка:
Здравствуйте, Oksana Gimmel, Вы писали:

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


S>>>Лично меня больше интересует другой вопрос: где Tuple? Т.е. кортеж с нулём значений?


J>>И зачем же он?


OG>Как нормальная замена void. Чтобы можно было функции, возвращающие значения, и функции, не возвращающие значения, однотипно полиморфно обрабатывать.


А мона пример??

Tuple GetTuple0() {...}
Tuple<int> GetTuple1 {...}

как можно полиморфно их обработать???
Re[6]: Использование tuple
От: Oksana Gimmel http://oksana-gimmel.blogspot.com/
Дата: 22.10.09 13:57
Оценка:
Здравствуйте, Jack128, Вы писали:

OG>>Как нормальная замена void. Чтобы можно было функции, возвращающие значения, и функции, не возвращающие значения, однотипно полиморфно обрабатывать.


J>А мона пример??


J>Tuple GetTuple0() {...}

J>Tuple<int> GetTuple1 {...}

J>как можно полиморфно их обработать???


Ну, например, в Haskell следующий код будет работать независимо от того, возвращает ли функция f кортеж, () или что-то ещё:
liftM f
asato ma sad gamaya
Re[6]: Использование tuple
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.10.09 14:53
Оценка: 6 (1) +1
Здравствуйте, Jack128, Вы писали:

J>А мона пример??


J>Tuple GetTuple0() {...}

J>Tuple<int> GetTuple1 {...}

J>как можно полиморфно их обработать???


Например оба можно вызвать через Func<T> (полагаю что второй строчкой метод, а не свойство, судя по Get).
А иначе первый вариант пришлось бы через Action, а второй через Func<T> вызывать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.