Pull to refresh
196
0
Михаил Бахтерев @mikhanoid

ИММ УрО РАН

Send message
Функциональное программирование больно той же самой болезнью: нужно проектировать задом наперёд. Сначала проектируется общий вид основной функции, а потом всё ниже и ниже проектируются вспомогательные. То же самое в ООП: сначала проектируем интерфейс, а потом пытаемся реализовать. И если интерфейс оказался неподходящим, то переделывать придётся весь код. Если высокоуровневая функция в функциональном подоходе оказалась неподходящей, то переделывать весь код придётся в гораздо большей степени, чем в императивном ООП.

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

C же освобождает от необходимости проектировать строго определённый интерфейс, конечно, от ответа на вопрос: что код должен делать? уйти вряд ли удасться, но ответ этот может быть достаточно нестрогим. Язык ориентирован на работу с данными, а не объектами. Если я совершу ошибку на этапе придумывания интерфейса, то мне никто не мешает напрямую залезть в структуру данных, и написать новый код для работы с ней, в том месте, где он нужен. Да, это грязно, это не математично, это нарушение кучи принципов, об этом не напишешь умную книжку, потому что это глупо, но это работет. И работает не только корректно, но и эффективно. Если подобный код мне нужен будет во многих местах - родилась абстракция - я его вынесу в соответсвующий модуль, и доступ к нему сделаю частью интерфейса.

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

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

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

Да, всё то же самое можно делать и на C++, например. Но тогда надо будет делать все данные и методы изначально открытыми, и возникает вопрос, а зачем тогда этот весь этот лишний классовый синтаксис?

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

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

И я должен буду выполнять много сложной работы по переписыванию кода, настолько сложной, что для этого появляются автоматизированные средства.
Тьфу блин. Чёртов грипп. 'писают всё написать на С' - это 'пытаются всё написать на C'.
Теоретически, всё верно. Но быстро выстроить хорошие абстракции очень сложно. Нет, конечно, если человек может делать это быстро, то у него превосходный интеллект, я бы сказал на уровне гениальности, но многи ли таких людей? Обычные люди, как убедительно показывают исследования в области истории науки, абстракции, не формулируют перед началом работы, а находят в процессе исследований. Абстракция - это обобщение свойств. А как можно начать обобщение свойств, когда компоненты ещё не написаны? Конечно, можно указать множество ситуаций, в которых можно прибегнуть к имеющемуся опыту. Например, кнопка там, или итератор, они и в Африке кнопка или итератор. Но что если программа к этому не сводится? Когда абстракции понятны - ООП рулит, очевидно и неоспоримо. Расширяться тоже можно просто и без проблем, реализуя уже найденные интерфейсы.

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

Насчёт free. Частое использование malloc/free - это рузультат неправильного проектирования, по моему мнению. И является болезнью больших проектов, в которых писают всё написать на C. Но C придумывался не для этого. Компоненты, написанные на C должны быть маленькими и максимально специализированными под решаемую задачу. Большая же решается через объединение компонентов через IPC. Это удобнее, чем придумывание иерархий, потому что позволяет вклиниваться и менять взаимодействие между компонентами как угодно.

Попробуйте воткнуть дополнительную обработку в pipe, или дополнительную обработку во взаимодействие между двумя объектами, когда один вызывает другой. Как минимум, придётся нечто наследовать, заново думать над тем, что должно быть виртуальным или не виртуальным. И так далее. Много работы. Может быть, именно поэтому, все сложные ОО системы рано или поздно приходят к тому, чтобы создать свою транспортную систему данных между объектами. Или воспользоваться существующей в виде базы данных или чего-нибудь, вроде, CORBA. Но это всё усложнения, усложнения и ещё раз усложнения. Которые надо осваивать, особенно при коммерческом программировании, а это всё уводит сознание от размышлений над другими методами организации вычислений. Существенно более гибкими и простыми. Ну вот сравнить хотя бы ту же CORBA с plumber'ом из Plan9 или Linda.

Поэтому я против ООП. Мне не нравится, когда навязываются сложности и неэффективности, да ещё такими ненаучными способами: вы хотя бы в одной книге по ООП видели сравнение предлагаемых способов решения тех или иных иных задач в этой парадигме с решением их в других?
КРоме того, GUI очень просто пишется на tcl - средстве, которое разработано, чтобы сшивать различные функции на C. Вот. Логика у него такова, что памятью в tcl программе очень просто управлять автоматически, а на tcl/tk GUI писать - совершенно очаровательное дело. Надо кнопку - пишем buttom и не мучаемся : ) Это я к тому, что все эти сложности с объектами, иерархиями, интерфейсами и прочим - совсем не обязательны, чтобы писать быстро и компактно.
Лёгкий GUI, написанный на Python превращается в очень тяжёлый GUI. Тяжёлый для пользователя, конечно: подтормаживает, память поджирает, процессор грузит. Не самая сложная программа, например, gajim, которая вобщем-то есть GUI для Jabber, может очень сильно начать греть процессор и грузить swap. Странно, но гораздо более сложный и функциональный gaim намного реактивнее. При этом, объём его кода всего на 30 процентов больше (а, напомню, протоколов он поддерживает в 6 раз больше). Вот такие пироги.
Почему? Люди любят учиться и разбираться, иначе они не пошли бы в программисты. Только разобраться во всём очень сложно. А работодатель заставляет разбираться не в самых эффективных средствах разработки. А раз человек выучил интерпретируемый язык, потому что все вокруг хотят от него того, чтобы он его выучил, и раз он может написать на нём программу, которая его устраивает, он и будет это делать.

Удручает только то, что вместо, например, огромной библии программирования на с++ или толстенной camelbook он бы мог прочесть тоненькую книжку о 'c', и толстую книжку об алгоритмах и структурах данных, да пару руководств по shell с описанием всяких утилит, вроде sed, что позволило бы ему писать более эффективно и быстро, да к тому же использовать все возможности существующего кода.
Эх : ( Опять нафлудил...
Тоже хочется про языки сказать. Вы не забывайте, что удобный и используемый язык - это не то же самое, что и язык за знание которого нанимают. C++, C#, Java, Perl - это то, за что платят, а платят потому что те, у кого есть деньги, частенько понятия не имеют, с чем они сталкивают своих программистов. Нет у них времени разобраться, поковыряться, и найти горы глюков, неконсистентностей, сложностей во всех этих раскрученных средствах разработки. Всё это есть в любях языках, например, в C кошмар с #include или деление на значение по адресу. Но чем меньше язык, тем этих глюков меньше. Perl, C++, C# и современная Java - большие языки.

Почему сложные языки популярны? Популярность этих средств книжна: чем толще книжка, тем язык круче - обычная мораль, обычного самца homo sapiens. Раз он смог освоить очень сложное средство, то он крут. Раз он смог освоить корявое средство, вроде C++, то он крут. Так уж получилось, что люди с деньгами оценвают не эффективность, а именно крутость того, кого нанимают, ибо в эффективности они понять ничего не могут, в силу иной профессиональной ориентации. А крутость, ещё раз, в программировании это означает умение программировать на сложных средствах. Нет, конечно, гораздо круче разбираться во всех алгоритмах, упомянутых Кнутом и Корнелом, но для этого уже нужно напрягать мозг и тратить время, и проверить эту крутость сложнее, потому что тогда наниматель должен в этом всём разбираться сам, а там терминов и абстракций гораздо больше, чем в семантике языка программирования, а связи между ними гораздо богаче, чем отношение isA.

Плюс людям с деньгами на семинарах промывают мозги, рассказывая сказки о том, насколько Java может быть эффективной, и насколько хорошо она может управлять нитями, и насколько хорошо памятью. Но начинаешь разбираться и: конечно, может, иногда, в некоторых случаях, специальных, которые часто встречаются в том коде, скорость работы которого не критична. Вы не забывайте, что насколько бы продвинутой была VM она обязана защитить runtime от действий программиста, а это горы лишних проверок при хоть сколько-нибудь нетривиальном обращении к массивам, или отсутсвие нормальных массивов в языке совсем. Но менеджер этого не знает, а обёртка у Java очень красивая.

Про C++ то же самое: смотрите какие чудные иерархии мы можем строить, а потом использовать совершенно безумные хаки, чтобы это всё нормально работало, cooool, давайте заставлять их строить всегда и везде, в любой беде. Давайте напишем про это кучу теоретических книжек, сделаем большие бабки на этом, и дадим кодерам возможность получать больше денег, за меньший объём осмысленной работы. Умные гомоморфные автоматические указатели со сборкой мусора рулятЪ и trueЪ. Это же такая хардкорная математика, и, как алгебраиста интеллектуально она меня очень привлекает, например. Всегда очень хочется прочитать новое издание advanced c++ programming. Но это всё красивые теории, а не практика.

Это всё к удобству и эффективности отношения не имеет. Просто, подкреплённое ленью менеджеров и ценой на компиляторы (компилятор C++ или C# НАМНОГО сложнее, чем компилятор C, естественно, их сложно воспроизвести, следовательно, на этом можно делать деньги), общественное мнение и работа на собственную крутость.

Между тем, на C в комбинации с продвинутой shell можно спокойно писать сложные web-приложения. И даже гораздо более сложные, чем на всех этих высокоуровневых С#, Java, C++, Python, которые загоняют программистов в древовидные структуры классов и библиотек. Потому что дают свободу. Посмотрите на все web службы для Plan9 - там только C и rc. Сложный ли там код? Гораздо проще, чем мне доводилось видеть в аналогичных службах, писанных на asp.net. Да, на C приходится писать free, освобождая память, но это единственная техническая сложность. Но по сравнению с чисто техническими проблемами при использовании классов - это такие мелочи.

Почему Linux'оиды чаще всего выбирают для программирования C и shell, а не кучу других языков, которые доступны для использования? У меня вот в gentoo установлено сейчас около 400 пакетов, и только около 50 составлены на чём-то отличном от C и shell. Не по историческим причинам совсем при разработке 'софта не из-за денег' используется именно эта комбинация, есть много свеженьких проектов, разработку которых начали в эпоху всего этого высокоуровневого разгула. Именно потому, что это достаточно компактные языки. Можно быстро выучить и запомнить все странности и глюки, можно получить свободу в оперировании данными. А это как раз то, что нужно, чтобы за короткое время стартануть, а потом эволюционно развивать проект.

Поэтому, возможно, самого C не станет со временем, но подобный ему язык со всеми прелестями (действительно прелестями, а не недостатками, как это считают теоретики), вроде указателей и плоского пространства имён, обязательно будет существовать. И будет массовым. Потому что простенько, эффективно и пригодно не только для того, чтобы писать ядра систем или виртуальные машины для языков программирования.
HT как раз успевает всё прокачивать, что доказывает тест на 2 модулях системы. Затыки возникают внутри процессора при обращении к общему контроллеру. И не понятно, как этого затыка избежать, ведь, ресурс один, что автоматически приводит к необходимости упорядочивать запросы и выполнять их последовательно. Когда amd сделает два контроллера памяти, то в процессоре будет 4 ядра, и они будут работать чепез общий L3, хотя и побитый на банки, ну, если верить картинкам, что приведёт к драке за этот кэш, потому что логика разруливания по банкам, всё равно, потребует упорядочивания запросов. Опять коллизии и ничего хорошего. Хых.

Но они погоняют свои мелкие тесты, вроде фотошопов, кодеков и архиваторов, и опять объявят процессоры самыми лучшими и высокопроизводительными на рынке PC. Ну ладно, они будут правы, Intel они опять сделают, скорее всего. НО, можно же более производительную архитектуру предложить, как показывают все эти эксперименты. И более дешёвую и энергоэффективную, как доказывает IBM своим BlueGene.

Хм. Неужели единственное, что сдерживает - страх перед тем, что программисты не освоят программирование для логически неоднородной системы? Хотя, тут давление, наверное, ещё со стороны компиляторщиков, которым надо продавать продукты с этикеткой OpenMP compatible. Да и Windows исторически многонитевая и мозги ей уже ничем не вправишь, кроме лома. Мдя, опять система ползёт в глючный аттрактор, как это произошло с x86.
Ну... Это. Спасибо за совет.
Так можно, конечно. Удручает отношение большинства к вопросам. Даже к тем, которые не требуют особых ресурсов и времени. Я за выходные смогу написать выкладывалку архивов с исходниками, чтобы они подсвечивались и перекрёстно ссылались: vim и ctags рулят. Но даже на такую мелкую деятельность очень многих сложно подопнуть.

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

Вот вчера был свидетелем, как солидный специалист в одной закрытой общалке, двум студентам промывал мозг на тему того, что ну не правы они, раз хотят свой движок для игр писать, нужно обязательно изучить движок Unreal и не шалить самостоятельно. Хм... Вот так вот. Нужно быть очень энергичными, чтобы не пасть духом и не разбиться о стену существующего.
Они в прямом смысле переписывают: набивают всё в TeX и компилируют в pdf.
Если я правильно понимаю, то они просто переписывают сейчас это всё в pdf. Некоторые первые статьи доступны уже в этом виде.
Но, на самом деле, все эти слова про Пролог и Хаскелл - мелочи, чтобы присобачить текст статьи к именам великих, ибо так надо, а не потому что хочется. Главная же мысль в том, что нужна просто файловая система. И что файлы, как инструмент, гораздо удобнее, чем сообщения, например. И что это удобство привязано к некому фундаменту, который уходит в способ рационального мышления и решания задач.
Пролог при том, что вылезают предложения Хорна, на которых он построен. А Хаскелл при том, что функции и редукция графов. Простейший пример. Пишем мы

gcc foo.c -o bar

На это можно посмотреть, как на

1. Логический вывод: существует утверждение gcc, утверждение foo.c из которых можно получить вывод - bar. Вобщем-то давно известно, что программы соответствуют выводам в интуиционистской логике. http://en.wikipedia.org/wiki/Curry%E2%80…

Это, конечно, не поиск наименьшего общего унификатора, но Пролог немного задевает. Хотя, наверное и не так сильно, чтобы выносить это в ключевые слова : ).

2. Вызов функции bar - получение этого значения при помощи применения функции gcc к функции-константе (хотя и не обязательно совсем) foo.c

3. Обычные операции с именованными данными: загрузить программу gcc, заставить её загрузить файл foo.c и выдать файл bar

Всё описывается одинаковой абстрактной схемой: gcc, foo.c -> bar. А подобные схемы исполнять параллельно очень удобно. Параллельные haskell'ы так делают. Или, например, make на это способен. Параллельная редукция lambda-графа, или графа зависимостей. Но это можно описывать не только в виде таких вот возвышенных материй, но и в виде операций с файлами:

for ((i = 0; i &lt 20; i = i + 1)); do gcc foo$i.c -o bar$i & pids[$i]=$!; done
for ((i = 0; i &lt 20; i = i + 1)); do wait pids[$i]; done

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

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

Сравнение с логикой: как только доказано, что a, b, c, d (сформированы соответствующие файлы) в утверждении a, b, c, d -> e, можно считать доказанным e (запросто формировать e). Поиск же доказательства или модели, удовлетворяющей аксиомам можно осуществлять параллельно. При этом очень даже совсем неплохо параллельно - тысячи математиков примерно этим заняты. Так почему бы эти конструкции вида a1, ..., an -> b1, ..., bn - предложения Хорна - из логики не использовать в описании алгоритмов, если ко всему прочему доказано, что они Тьюринг-полные?

Сравнение с функциональным программированием: речь идёт о банальном ленивом вычислении значения. Только не на уровне базовых конструкций функциональных языков, а на уровне файлов с произвольным содержимым. Haskell, как shell : ) Никто ещё не пробовал?

Вот. При этом всём файлы достаточно удобны при числодробительных вычислениях - это некие куски данных с произвольным доступом. Как раз то, что нужно чтобы матрицы умножать. Гораздо удобнее списков или небезопасных массивов из мира функциональных языков. Да и просто привычнее.

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

Вобщем примерно всё вот так. Подробнее, конечно, надо. Но времени нет : (
Однако, интересный способ превращать целые страны в сырьевые придадки: давайте запретим людям развиваться и обмениваться информацией, хопа, и лет через 10 нового ничего появляться не будет. А вот интересно, что считается internet'ом? Если я контент начну раздавать через gopher, что мне за это будет?
Я и так не занимаюсь администрированием серверов. Так что это в вашем замечении о диванчике нет никакого смысла.
Да. Но контроль осуществляется индивидуально для процессов. Когда ресурсы выделяются, всегда проверяется current->signal->rlim - локальный массив задачи с описанием ограничений, который просто от лидера в группе наследуется, при создании новых процессов.
Если смотреть сюда http://shootout.alioth.debian.org/sandbo…, то можно заметить по крайней мере двухкратное отстование java -server в скорости от gcc (шикарный результат в исполнении байткода для такого гибкого и мощного языка, как java, но это, всё равно, в 2 раза медленней). Кроме того, виртуальная машина жадная до памяти.

Information

Rating
Does not participate
Location
Екатеринбург, Свердловская обл., Россия
Registered
Activity

Specialization

Backend Developer, Научный сотрудник
Applied math
System Programming
Machine learning
Compilers
Scheme
C
Assembler
Linux
Clojure
Haskell