Здравствуйте, eao197, Вы писали:
E>Здравствуйте, BulatZiganshin, Вы писали:
BZ>>>насколько я понимаю, в нём реализовано подмножество возможностей явы.
BZ>>вот кстати, сейчас вычитал, что eiffel/c++ нельзя делать виртуальными полиморфные методы, а в c#/яве — можно. так что это всё же две разные вещи, и сложность явы вызвана попыткам полностью реализовать все возможные сценарии их взаимодействия, а простота c++/эйфеля — тем что взаимодействие между ними в полном объёме не реализовано
E>Что такое "виртуальные полиморфные методы"?
насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
Здравствуйте, BulatZiganshin, Вы писали:
BZ>>>вот кстати, сейчас вычитал, что eiffel/c++ нельзя делать виртуальными полиморфные методы, а в c#/яве — можно. так что это всё же две разные вещи, и сложность явы вызвана попыткам полностью реализовать все возможные сценарии их взаимодействия, а простота c++/эйфеля — тем что взаимодействие между ними в полном объёме не реализовано
E>>Что такое "виртуальные полиморфные методы"?
BZ>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
Ну и что здесь сложного? (В качестве уточнения задачи: какой метод может быть общим у массива и стека, за исключением size и capacity?)
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, eao197, Вы писали:
E>>Что такое "виртуальные полиморфные методы"?
BZ>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
Не понял, а чем это от просто виртуальных методов отличается? Т.е. чем плюсовый virtual-метод от метода явовского объекта отличается? И там и там vmt, которая даёт полиморфность вызовов.
Можешь кусок кода набросать?
Здравствуйте, BulatZiganshin, Вы писали:
E>>Все вышесказанное было интересно, но вот нет у меня веры в то, что ФП -- это нормальный подход к программированию. Поскольку программирование для меня было и есть -- это алгоритмизация, т.е. запись решения задачи в пошаговой форме. Как бы мы не крутились с декларативной нотацией все равно -- если список файлов из каталога должен быть получен до выполнения операций над файлами, то ничего не поделаешь -- это придется явно выразить в коде программы.
BZ>знаешь, то, для чего в 50-е писали целые императивные программы, сейчас может быть выражено в excel или mathematica с помощью набора формул без необходимости явно записывать все эти циклы и порядок вычислений. решаемые задачи не изменились, но для них нашли более краткую форму, которая описывает только то, какой результат нам нужно получить. Excel иногда сравнивают с таким функциональным языком, где нет возможности описывать свои функции
Подобный экскурс в историю не выглядит доказательством чего-либо. Был бы в 50-е Excel, мало кто бы писал императивные программы. Вся проблема в том, что в 50-е годы существование Excel-я было вообще невозможно. Как еще относительно недавно было невозможно делать сложные вычисления в Excel на 486-x с 4Mb RAM на борту.
Так что суть не в том, что нашли более краткую форму. А в том, что благодоря не требовательному к вычислительным ресурсам императивному подходу смогли довести техническую базу до достаточной для декларативных вычислений мощности.
BZ>в случае твоего sms-шлюза, как я понимаю, действия очень просты — получить sms/отправить sms,
Да, любая программа может рассматриваться как окошко с двумя кнопками -- большой зеленой "Работать" и маленькой красной "Стоп". Впрочем, маленькая красная не всегда нужна.
BZ>а вся сложность именно в функциях, определяющие содержимое отправляемых sms и логику их отправки, т.е. это чисто функциональные вычисления. и вопрос состоит имхо в том, что ты не знаешь, как эти вычисления описать, чтобы было читаемо, эффективно и т.д.
Сейчас я не знаю, зачем мне знать, как это описать еще в какой-то другой форме.
E>>У Ричарда Гэбриеля есть хорошее эссе "Reuse versus Compression", в котором он указывает, что ООП -- это не столько путь к повторному использованию, сколько к компрессии программ. Мне кажется, что ФП (по крайней мере по результатам моих попыток познакомиться с Haskell и OCaml) есть не более чем еще один способ компрессии, даже более сильный, чем ООП. А недостатком сильно ужатого текста является сложность его восприятия.
BZ>стоп. а ты сам можешь писать на них неужатые программы? мне кажется, что ты просто пытался читать чужой код и его было сложно понять. однако имхо причина этого не в том, что ФП *заставляет* ужимать код, а в том, что она предоставляет новые сжатые идиомы. пока ты их не освоил — этот код кажется такой же абракадаброй, как например для несишника *p++=*q++ (кстати, ты будешь смеяться, но я сейчас в хаскел-кафе имею дело именно с такими людьми). по мере того, как ты осваиваешь хаскел и выучиваешь эти идиомы, функциональный код будет казаться более естественным и понятным. ну а то, что для тебя неестественно, всегда можно записать в более многословной манере. я напримере, никогда не пользуюсь foldr — голова не пареваривает; вместо этого описываю явную рекурсию. или вот, к примеру:
BZ>
BZ>add_version str = if verbose
BZ> then str++" v2.0"
BZ> else str
BZ>или
BZ>add_version = verbose &&& (++" v2.0")
BZ>
BZ>мне лично вторая версия нравится больше как описывающая только суть дела и не содержащая ничего лишнего. опять-таки, в первое время работы с хаскелом меня коробило от point-free стиля (когда при описании функций опускаются их параметры), сейчас я отношусь к нему спокойно
Смысл компрессии в упомянутой работе Гэбриеля в том, что благодоря неявному контексту на котором написан некоторый фрагмент кода, семантика фрагмента становится значительно больше семантики одельных символов фрагмента кода. Применительно к ФП это означает, что когда кто-то пишет что-то типа:
То смысл всего выражения оказывается гораздо более емким, чем каждой из его частей. Так вот, на мой взгляд, требование к пониманию контекста в ФП выше, чем в ООП. И не факт, что это хорошо.
От чего я действительно устал за время программирования, так это от наличия разных уровней сложности в одном языке. Мне уже не кажется нормальным, когда кто-то выбирает фичу языка A потому, что он не понимает фичу B. Ты сам сказал, что foldr для тебя не просто -- имхо, это уже симптом. Для кого-то не просты шаблоны C++. Следствием этого являются библиотеки, вроде Boost. Про которые говорят -- не нужно в них влазить, не обязательно даже знать C++ в такой же степени, в которой его знают разработчики Boost-а, просто используй его и будет тебе счастье.
По-моему, это не правильный подход. Язык должен предоставлять набор логичных и простых конструкций. И чтобы новичок в языке не глядел на код гуру с восхищением: "Ух ты, оказывается и так можно! Вау!". И новичок и гуру должны использовать одни и те же конструкции. Разница должна быть не в ужатости кода, а в том, насколько код соотвествует поставленной задаче.
Возвращаясь к Eiffel нужно сказать, что в нем Мейер на удивление близко подобрался к данному идеалу. Очень мощный язык, но без слишком темных углов. Если бы не странный (по крайней мере на мой текущий взгляд) механизм исключений, со всеми его остальными недостатками можно было бы смириться.
E>>Так что я пока подожду, посмотрю, что проиходит, чем же закончится всплеск интереса к ФП (если он вообще есть за пределами программистких форумов и раскрученных блогов).
BZ>а мне казалось, что это не всплеск, а постепенное нарастание со временем. может, оно ускорилось в последние лет 10 в связи с увеличением мощности персоналок и перспектив многоядерности. по крайней мере, аудитория haskell-cafe сейчас в абсолютном выражении растёт быстрее, чем раньше, а в относительном — наверно так же. просто это экспоненциальный рост
Ну пусть будет так.
So it goes
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, BulatZiganshin, Вы писали:
BZ>>>>вот кстати, сейчас вычитал, что eiffel/c++ нельзя делать виртуальными полиморфные методы, а в c#/яве — можно. так что это всё же две разные вещи, и сложность явы вызвана попыткам полностью реализовать все возможные сценарии их взаимодействия, а простота c++/эйфеля — тем что взаимодействие между ними в полном объёме не реализовано
E>>>Что такое "виртуальные полиморфные методы"?
BZ>>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
E>Ну и что здесь сложного?
не знаю
E>(В качестве уточнения задачи: какой метод может быть общим у массива и стека, за исключением size и capacity?)
типа "не больно-то и хотелось"? всё-таки это пример лакуны во взаимодействии классов и generics
E>>>Что такое "виртуальные полиморфные методы"?
BZ>>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
К>Не понял, а чем это от просто виртуальных методов отличается? Т.е. чем плюсовый virtual-метод от метода явовского объекта отличается? И там и там vmt, которая даёт полиморфность вызовов. К>Можешь кусок кода набросать?
generic класс — это по c++ templated class. примерно так:
Здравствуйте, BulatZiganshin, Вы писали:
E>>>>Что такое "виртуальные полиморфные методы"?
BZ>>>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
E>>Ну и что здесь сложного?
BZ>не знаю
Вот и я не знаю
E>>(В качестве уточнения задачи: какой метод может быть общим у массива и стека, за исключением size и capacity?)
BZ>типа "не больно-то и хотелось"?
Ну мы, я надеюсь, не достигли еще такой степени нетерпимости друг к другу, чтобы общаться подобным образом.
Просто пример наследования стека от вектора является весьма показательным с другой точки зрения -- необходимости сокрытия интерфейса вектора в стеке, поскольку работа со стеком в корне отличается от работы с массивом: совсем другие операции добавления/извлечения элементов, отсутствие прямого доступа к элементам контейнера и пр.
Т.е. это случай, когда наследование используется для наследования реализации, но не для увеличения специализации путем наследования интерфейса с перекрытыми реализациями методов.
Что в С++ решается, например, с помощью protected/private наследования:
template< class G >
class vector_t
{
public :
void append( const G & );
const G & at( size_t index ) const;
void insert( size_t index, const G & );
void remove( size_t index );
size_t size() const;
...
};
template< class G >
class stack_t : protected vector_t< G >
{
public :
const G & top() const { assert( size() ); return at( size() - 1 ); }
void pop() { assert( size() ); remove( size() - 1 ); }
void push( const G & item ) { append( item ); }
using vector_t::size; // Не счет этого не уверен, давно не пользовался.using vector_t::capacity;
};
В Eiffel это же дело решалось бы путем:
a) переименования методов унаследованных из VECTOR (например, vector_append, vector_insert, vector_remove) и, затем
b) экспортом части методов только классу NONE.
Что-то типа:
class
STACK [G]
inherit
VECTOR [G]
rename
append as vector_append,
insert as vector_insert,
at as vector_at,
remove as vector_remove
export
{NONE} vector_append, vector_insert, vector_at, vector_remove
end
feature
top: G is
require
non_empty: 0 < size
do
Result := vector_at (size)
ensure
one_more: (old size + 1) = size
end
pop is
require
non_empty: 0 < size
do
vector_remove (size)
ensure
one_less: old size = (size + 1)
end
push (item: G) is
do
vector_insert (item)
ensure
one_more: (old size + 1) = size
end
end
BZ>всё-таки это пример лакуны во взаимодействии классов и generics
Да не видно здесь ничего страшного. Давай, например, возьмем два класса: вектор (vector_t) и разреженный вектор (sparse_vector_t) в котором каждый четный элемент равен 0. В классе vector_t нужно объявить виртуальный метод at(), а в sparse_vector_t -- переопределить его. И все будет работать -- ведь это же основной принцип ООП, и generic-и здесь вообще не при чем.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, BulatZiganshin, Вы писали:
E>>>>Что такое "виртуальные полиморфные методы"?
BZ>>>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
К>>Не понял, а чем это от просто виртуальных методов отличается? Т.е. чем плюсовый virtual-метод от метода явовского объекта отличается? И там и там vmt, которая даёт полиморфность вызовов. К>>Можешь кусок кода набросать?
BZ>generic класс — это по c++ templated class. примерно так:
[cut]
BZ>но этот код работает видимо, я что-то понял неправильно. утверждение взято с 22-й страницы http://research.microsoft.com/~akenn/generics/TransposingFToCSharp.ppt
Что-то я так до конца не могу понять что ты хочешь сделать и зачем. Какие-то заморочные теоретические изыски имхо.
Что тот код выдаст? 1 или 2? Есть ощущение что 2, но цпп под рукой нет
Ты хочешь чтобы метод определялся типом ссылки или переопределялся через vmt? Первое имхо какой-то антиполиморфизм, а второе — стандартное поведение "традиционных" ООП-языков аля ява/шарп/плюсы (generic тут только на наследование влиять будет, имхо, кстати в плюсах будет ковариантность для T соблюдаться?)
Спасибо за очень интересную тему, я и сам постоянно пытаюсь сформулировать подход, который бы позволил проще и короче писать безопасный и достаточно быстрый код.
Некоторый замечания:
На странице Почему я ищу новый язык? написано, что "for_each не специфицирует порядка обхода элементов". На первый взгляд в 25.1.1 сказано, что "for_each() аpplies f to the result of dereferencing every iterator in the range [first, last), starting from first and proceeding to last — 1.".
Операторы записываются не result_type operator(argument_type item), а result_type operator()(argument_type item); class selector_t не может быть объявлен там же, где используется for_each() (кстати, это мне тоже совсем не нравится: функторы приходится объявлять за пределами процедуры с for_each(), из-за этого потом трудно читать код).
В "Что не так с C++?" я бы на первое место поставил саму возможность "unspecified behavior" и "implementation-defined behavior", ведь когда разрабатывается надежная система, все должно быть специфицировано. Хотя мне говорили, что даже в таком языке как Ada, рекомендуемом для безотказных систем, порядок вычисления аргументов функции тоже неопределен (пусть поправят те, кто на ней писал), поэтому есть причины сомневаться, что вообше существует язык программирования, удовлетворяющий всем поставленным критериям. И это точно не Eiffel.
Так что пока С++ выигрывает по очкам (IMO). Может имеет смысл подумать о гибридных системах: C++ — системные библиотеки, критические по скорости процедуры, старый код, а сверху над ними что-нибудь более удобное и очень простое (что это может быть я и сам не знаю — пробовал только Python, поэтому не столько советую, сколько спрашиваю)?
Здравствуйте, BulatZiganshin, Вы писали:
E>>>>Что такое "виртуальные полиморфные методы"?
BZ>>>насколько я понимаю, ты определяешь generic класс, скажем массив, затем его наследника скажем стек. определяешь виртуальный метод в массиве, даёшь ему другую реализацию в стеке. затем определяешь переменную типа сслыка на массив, присваиваешь ей ссылку на стек, и вызывываешь этот метод. хочешь попробовать?
К>>Не понял, а чем это от просто виртуальных методов отличается? Т.е. чем плюсовый virtual-метод от метода явовского объекта отличается? И там и там vmt, которая даёт полиморфность вызовов. К>>Можешь кусок кода набросать?
Я думаю, ты немного спутал термины. "Виртуальные полиморфные методы" это не виртуальные методы шаблонного ("полиморфного") класса, а шаблонные ("полиморфные") методы класса (не важно, обычного или шаблонного), т.е.
// C#abstract class Base
{
public abstract T F<T,U>(T t, U u) where U : T;
}
class Derived: Base
{
public override X F<X,Y>(X x, Y y) // Ok
{
return y;
}
}
The last good thing written in C was Franz Schubert's Symphony No. 9.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, BulatZiganshin, Вы писали:
E>>>Хочется более простого и стройного языка, чем Java. Без спецификации исключений, без различия между int и Integer, с более простым обобщенным программированием (что-нибудь в духе шаблонов C++ или обобщенного программирования в Eiffel).
BZ>>кстати, насколько я понимаю, проблема в том, что ООП и полиморфизм — это разные вещи.
E>Попробуй это Бертрану Мейеру рассказать. Я думаю он очень удивится.
Не только он. Еще Alan Key акцентирует внимание на "extremly late binding". То есть, полиморфизм это наше всё.
Здравствуйте, BulatZiganshin, Вы писали:
E>>Хочется более простого и стройного языка, чем Java. Без спецификации исключений, без различия между int и Integer, с более простым обобщенным программированием (что-нибудь в духе шаблонов C++ или обобщенного программирования в Eiffel).
BZ>ООП и полиморфизм — это разные вещи.
Здравствуйте, mnf, Вы писали:
mnf>Некоторый замечания: mnf>На странице Почему я ищу новый язык? написано, что "for_each не специфицирует порядка обхода элементов". На первый взгляд в 25.1.1 сказано, что "for_each() аpplies f to the result of dereferencing every iterator in the range [first, last), starting from first and proceeding to last — 1.".
Именно. Гарантируется, что будет осуществлен доступ к каждому из элементов, но не описывается, в каком порядке.
И я где-то, кажестя у Саттера, читал предупреждение об этом моменте. Мол, смотрите, появится какой-нибудь хитрый for_each, который распараллелит обход на несколько процессоров и кирдык.
С другой стороны, ситуация какая-то маразматическая -- просто так нельзя делать параллельный for_each, т.к. функтор может быть не thread-safe. Так же, в STL нет метода обхода контейнера от начала к концу, аналогичному for_each, для которого порядок обхода гарантировался бы (либо я не знаю). Есть transform, но он для других целей.
Или, может быть, тот C++ гуру предупреждал, что for_each может делать обход от last-1 к first. И поэтому нельзя закладываться на порядок обхода.
В общем, было что-то мутноее с этим делом.
mnf>Операторы записываются не result_type operator(argument_type item), а result_type operator()(argument_type item);
Спасибо.
mnf>class selector_t не может быть объявлен там же, где используется for_each()
На самом деле там просто приведен схематичный код, без разделения на области видимости
mnf>Так что пока С++ выигрывает по очкам (IMO). Может имеет смысл подумать о гибридных системах: C++ — системные библиотеки, критические по скорости процедуры, старый код, а сверху над ними что-нибудь более удобное и очень простое (что это может быть я и сам не знаю — пробовал только Python, поэтому не столько советую, сколько спрашиваю)?
У меня пока не было задач, где можно было бы что-то подобное применить.
mnf>Удачи в поисках,
Спасибо.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, crable, Вы писали:
C>Я думаю, ты немного спутал термины. "Виртуальные полиморфные методы" это не виртуальные методы шаблонного ("полиморфного") класса, а шаблонные ("полиморфные") методы класса (не важно, обычного или шаблонного), т.е.
C>
C>// C++
C>struct foo
C>{
C> template <typename T> void f() {} // компилируется
C> virtual template <typename T> void b() {} // а здесь ошибка
C>};
C>
C>
C>// C#
C>abstract class Base
C>{
C> public abstract T F<T,U>(T t, U u) where U : T;
C>}
C>class Derived: Base
C>{
C> public override X F<X,Y>(X x, Y y) // Ok
C> {
C> return y;
C> }
C>}
C>
Очень похоже на то.
Но какой в этом деле смысл и как это используется?
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
mnf>>На первый взгляд в 25.1.1 сказано, что "for_each() аpplies f to the result of dereferencing every iterator in the range [first, last), starting from first and proceeding to last — 1.".
E>Именно. Гарантируется, что будет осуществлен доступ к каждому из элементов, но не описывается, в каком порядке. E>И я где-то, кажестя у Саттера, читал предупреждение об этом моменте. Мол, смотрите, появится какой-нибудь хитрый for_each, который распараллелит обход на несколько процессоров и кирдык.
Можно ссылочку? По-моему, в Стандарте "starting from first and proceeding to last — 1" и означает "начиная c первого и продолжая до last — 1" — т.е. порядок доступа,
Что не так с моим английским? Кстати, в SGI описании STL for_each() это даже специально подчеркнуто "аpplications are performed in forward order, i.e. from first to last".
Здравствуйте, BulatZiganshin, Вы писали:
BZ>кстати вспомнил, в hopl-c++ и Степанов, и Страустрап в один голос утвержают, что темлплейты — это не ООП фича, и что сейчас С++ — гибридный язык
С++ был гибридным языком с самого начала, еще даже до добавления в язык шаблонов.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, mnf, Вы писали:
mnf>>>На первый взгляд в 25.1.1 сказано, что "for_each() аpplies f to the result of dereferencing every iterator in the range [first, last), starting from first and proceeding to last — 1.".
E>>Именно. Гарантируется, что будет осуществлен доступ к каждому из элементов, но не описывается, в каком порядке. E>>И я где-то, кажестя у Саттера, читал предупреждение об этом моменте. Мол, смотрите, появится какой-нибудь хитрый for_each, который распараллелит обход на несколько процессоров и кирдык.
mnf>Можно ссылочку? По-моему, в Стандарте "starting from first and proceeding to last — 1" и означает "начиная c первого и продолжая до last — 1" — т.е. порядок доступа, mnf>Что не так с моим английским? Кстати, в SGI описании STL for_each() это даже специально подчеркнуто "аpplications are performed in forward order, i.e. from first to last".
Боюсь, что я сейчас уже не вспомню, где я это видел.
Здравствуйте, eao197, Вы писали:
E>Удалось оформить причины, вынудившие меня задать этот вопрос, в виде небольшого раздела моей home page
E>В рамках этого же мероприятия получилось более плотно познакомиться с языком Eiffel.
То есть пока одна альтернатива ждать пока D устаканится?
Здравствуйте, FR, Вы писали:
E>>Удалось оформить причины, вынудившие меня задать этот вопрос, в виде небольшого раздела моей home page
E>>В рамках этого же мероприятия получилось более плотно познакомиться с языком Eiffel.
FR>То есть пока одна альтернатива ждать пока D устаканится?
Ну, с учетом того, что Nice приказал долго жить, из нынешних стабильных языков можно выбирать, разве что, между Java и C#. Но я бы пока предпочел подождать стабилизации D и Scala. Хотя возможностей D1.0 мне сейчас хватит с лихвой, дело стоит за инструментами и библиотеками для него.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.