Re: programming languages
Evening, Andrei.
Andrei Sosnin <demonsly@hot.ee> 14:31 13/1/2003 wrote:
>> Вот именно. Всегда будет иметь _строго_ _определенный_ тип. А как насчет
>> _произвольного_ типа, главное, чтобы он совпадал с типом аргумента функции
>> f, который тоже _произвольный_? Как сделать hard-coded решение на шаблонах
>> для конкретной задачи - мне понятно. Мне также понятно, как можно возвести
>> это решение в ранг "универсального"/"библиотечног", обвешав его "своими", "удобными"
>> реализациями контейнеров, итераторов и т.п. (по этому пути пошел STL). Мне
>> непонятно, как заставить это решение работать в самом общем случае. Более
>> того - я уверен, что реализовать такое решение невозможно.
AS> Насколько общим должно быть решение? То есть где кончается type-safe и
AS> начинается (иногда даже абсурдная) type-independent? :-) (См. также
AS> комментарии ниже)
Смотри, какая штука - если у меня есть "нормальный" map, то с его помощью я
могу обработать произвольный список произвольной функцией при условии, что
тип эл-то списка совпадает с типом параметра функции. Это - очень общее
решение. Список - любой, функция - любая. Теперь берем язык, в котором
средства создания контейнерных типов изначально не предусмотрены. И
получается, что для данной конкретной реализации сущности "список" можно
придумать худо-бедно применимую реализацию "map", но решение не получается
общим и какой ценой оно может быть использовано?
А type-safe мною упоминается ровно потом, что без него - не получается
адекватности того, что предлагаешь ты, и того, что предлагаю я.
>> AS> for_each(my_list.begin(), my_list.end(), addvaluesbyone());
>> AS> И при этом класс addvaluesbyone не перегружает оператор () или
>> этот
>> AS> перегруженный оператор не принимает объекты типа MyClass, а только int
>> AS> или float, или user_defined, то будет выдана ошибка компиляции.
>> Вот. А если функция принимает, к примеру, аргумент типа ConcreteSuperclassOfMyClass -
>> будет ли ошибка или нет? Если нет - то правильно ли это? :) Я считаю, что
>> это - неправильно, приводит к трудноловимым ошибкам и костылям.
AS> Если ты специально не написал такого перегруженного оператора в
AS> addvaluesbyone(), то ошибка будет (я это подчеркнул). Если же
AS> родительский класс - конкретный (не виртуальный), и в addvaluesbyone()
AS> перегружен оператор(), тогда не будет, и вероятно, ты именно этого и
AS> хотел (то есть, видимо, добавление одного к элементам типа
AS> ConcreteSuperclassOfMyClass допустимо.
Да. Проблемы начинаются тогда, когда поведение addvaluesbyone для MyClass и
ConcreteSuperclassOfMyClass - различно (такое очень часто пишут
горе-програмисты при наследовании, т.е. реализуют в вирт. методе поведение,
нарушающее семантику, заложенную в предка). Что приводит к трудноловимым
ошибкам и костылям.
AS> Как ошибка такого рода решается в других языках? Разве может
AS> компилятор решать за тебя, к чему ты хотел добавлять единицу, если ты
AS> сам это запрограммировал, инстанцировав addvaluesbyone() для класса
AS> ConcreteSuperclassOfMyClass? :-) Без этого (без инстанцирования, т.е.
AS> конкретизации типов для шаблона) addvaluesbyone() и для MyClass не
AS> будет работать.
В других языках может не быть иерархии сущностей и понятия наследования в
том виде, в котором оно есть в С++... Соответственно, может не быть и
проблемы.
>> Ты не замечаешь разницы между тем, что написал я, и тем, что написал
>> ты? У
>> меня функция f имеет тип "отображение из X в Y", функция g имеет тип
>> "отображение из Y в Z", результат compose имеет тип "отображение из X в Z",
>> где X,Y,Z - _произвольные_ типы. Главное, чтобы тип результата f и
>> аргумента g совпадал.
>> Как мне при помощи твоего "решения" скомбинировать функции из float в
>> string и из string в int ?
AS> В С для этого пишется отдельная функция, в С++ - шаблон с инстанцированием.
AS> template <class RetType, class RecType, class RepType = double>
AS> class f_func{
[skip пример, который "мимо темы"]
AS> Затем, f_func специализируется для каждого "необычного" типа (вроде
AS> string), а также - обязательно - для принятия самой себя в качестве
AS> аргумента:
[skip]
AS> Главное условие (в простейшем случае), чтобы существовал конструктор
AS> для return_type, принимающий RepType. Конечно, для перевода float ->
AS> string, string -> int понадобятся соответствующие специализации
AS> класса, но для int -> long нет.
Спасибо, мне не нужно реализовывать приведение типов. Мне нужна композиция
функций. Есть знакомство с математическим смыслом этого понятия?
Мне нужен инструмент, который позволяет взять функцию string -> int
(например, с семантикой "кол-во строк в файле с указаным именем"), функцию
int -> string (например, с семантикой "число прописью"), сделать "compose
spell_number count_lines" и получить сущность типа "функция из string ->
string" с семантикой "число строк в файле с указаным именем прописью".
Напомню, что все это - иллюстрация того, что такое "first-class funcitons".
AS> Если это можно сделать на Haskell без дополнительного кодирования, то
AS> только потому, что это просто было включено в "стандартную
AS> библиотеку". Конкретная реализация перевода string -> int, float ->
AS> string останется на совести разработчиков библиотеки. То есть можно
AS> сказать спасибо разработчикам, что не поленились. Но это заслуга не
AS> языка, а разработчиков интерпретатора и стандартной библиотеки. Может
AS> это одни и те же люди, но это разные их заслуги.
Имхо, кое-кто не понял задачи, решаемой примером. И этот кое-то - не я :)
>> AS> На С++ это можно записать совершенно также, но можно применить и ООП-метод.
>> Увы, скорее "так же", а не "также". Так же плохо.
[skip]
>> AS> инстанциация должна проходить так, например: composite_func<float>();
>> Ничего, что у тебя получаются только функции из X в X? :)
AS> См. выше... Если немного потрудиться, можно заставить и из float в
AS> string переводить. Только не понимаю, зачем необходимо из класса Мух
AS> делать класс Слонов, а потом еще из Слонов делать Комаров? Если нужно
[skip]
Ты решаешь совсем не ту задачу.
AS> уж совсем type-independent (почувствуй разницу с type-safe), то нужно
AS> будет немного потрудиться. Но обычно это не так уж необходимо. Если мы
AS> пишем библиотеку математических вычислений, то нам не нужно совсем уж
AS> произвольный класс складывать и умножать с другим. Определенно, мы
AS> будем иметь дело с классами и типами, которые могут представлять числа
Елки же ж моталки. Ну что такого сложно в выражении "получить функцию z как
результат композиции функций f и g, где g - .... (см. выше)"? Неужели я
настолько путано все изложил?
AS> Да нет, именно "на С++ можно все то же самое". ;-) Просто, когда с
AS> пеной у рта пытаются что-то доказать, думают, что это считается самым
AS> лучшим из лучших. Я же лишь пытаюсь доказать, что С++ - язык
AS> достаточно универсальный, чтоб позволить делать очень многое. И чтобы
AS> не подумали чего большего, заявляю, что С++ все равно *может быть*
AS> неидеальным.
Пока что примеры бьют немного мимо цели ...
AS> Но, между тем, я придерживаюсь мнения, что С++, по крайней мере, не
AS> менее достоен чести быть широко используемым языком программирования,
AS> по сравнению с языками ФП. То есть, ФП не обязательно должно быть
AS> предпочтительнее ИП (ООП, ...).
Да, C++ - это оцень хороший target для компиляторов с языков высокого
уровня, с этим сложно поспорить. Хотя C - легковеснее и лучше.
>> Да-да.... А кто до сих пор сомневается - плохо ее читал, так? :)
AS> Может, просто не читал... Я просто вижу, что ты (как и я) не знаешь
AS> тонкостей языка, делающих возможным то, чего не позволяли раньше
AS> императивные языки.
Я почему-то вижу несколько иное ...
--
Dmitry Astapov //ADEpt E-mail: adept@umc.com.ua
GPG KeyID/fprint: F5D7639D/CA36 E6C4 815D 434D 0498 2B08 7867 4860 F5D7 639D
Reply to: