Функциональное программирование всегда привлекало меня в противопоставлении к императивному.
Я очень часто обсуждаю различные аспекты функционального программирования на различных ветках на Базарной площади.
Но хотелось бы собрать всех заинтересованный этой темой в одной ветке.
Я думаю что настало время открыть такую тему. И вот почему.
Исторически функциональное программирование появилось практически вместе с императивным.
Вторым языком после фортрана был лисп.
Но увы, функциональное программирование надолго было уделом исследовательских институтов или специализированных приложений (Искусственный Интеллект)
Конечно не надо считать весь мир дураками из за того что развитие пошло по пути языков Алгол семейства.
Для этого были вполне обьективные причины. Функциональные языки слишком близки к человеку и слишком далеки от машины.
Они сьедают в десятки раз больше рессурсов чем императивные языки.
Вспомните претензии, предявляемые к java - первому императивному языку с виртуальной машиной и сборщиком мусора, толкаемому большими корпорациями в mainstream.
Жутко тормозит, и жрет всю память какая есть. А ведь функциональные языки (далее ФЯ) все без иключения имеют сборщик мусора, виртуальную машину.
Многие из них (семейство лисп) еще и динамические, что только усугубляет положение.
Вполне естественно что появившись более полусотни лет назад они надолго опередилли свое время.
Для широкого распространения ФЯ нужны гигабайты дешевой памяти и гигагерцы дешевых процессоров.
Прошло более 50 лет, прежде чем такие требования к железу стали реальностью.
Это время наступило. СЕЙЧАС.
Добро пожаловать в новую эру программирования.
По поводу производительности Лиспа в вычислительных задачах была интересная статья:
http://http.cs.berkeley.edu/~fateman/papers/lispfloat.ps
Ответ на
»сообщение 2547« (Руслан Богатырев)
___________________________
Безусловно летал, но, согласитесь, если хотите быть до конца корректным, Лисп на нелисповой архитектуре (которые мы все у себя имеем) вряд ли способен конкурировать с теми средствами, которые под эту мейнстрим-архитектуру заточены. Приходится подстилку класть. Увы-увы.
Похоже, что моя предыдущая проповедь пропала зря... Если уж так велика потребность поучаствовать в дискуссии, почему бы не ознакомиться с реальным положением вещей, прежде чем производить мегабайты флуда, опровергать который в сотый раз становится просто неинтересно?
Во-первых, софт проекта RAX работал на самом обычном железе (в этой дискуссии уже упоминались и сайт NASA с отчетом об эксперименте, и сайт разработчика системы, Erann Gat --- кому интересно, найдет сам) в среде LispWorks (доработанной, подробности см. там же).
Во-вторых, о "подстилке". Коль скоро здесь не принято самостоятельно изучать предмет спора, прежде чем делать выводы, придется показывать "на пальцах". Берем самую обычную бесплатную версию LispWorks 4.3 и пробуем в ней следующий примерчик:
(defun f (a b)
(+ (* a b) 3.14))
Набрать его можно прямо в окне listener'а, после чего сделать
(compile 'f)
(disassemble 'f)
В ответ получаем примерно следующее:
2145567A:
0: 3B25BC150020 cmp esp, [200015BC] ; T
6: 7637 jbe L2
8: 80FD02 cmpb ch, 2
11: 7532 jne L2
13: 55 push ebp
14: 89E5 move ebp, esp
16: 50 push eax
17: 8B4DFC move ecx, [ebp-4]
20: 0A4D08 orb cl, [ebp+8]
23: 752B jne L3
25: 8B4508 move eax, [ebp+8]
28: C1F808 sar eax, 8
31: 0FAF45FC imul eax, [ebp-4]
35: 701F jo L3
L1: 37: B97C004521 move ecx, 2145007C ; 3.14
42: 0AC8 orb cl, al
44: 7523 jne L4
46: 89C7 move edi, eax
48: 81C77C004521 add edi, 2145007C ; 3.14
54: 7019 jo L4
56: FD std
57: 89F8 move eax, edi
59: C9 leave
60: C20400 ret 4
L2: 63: E8A420BFFE call 20047762 ; #<function 20047762>
L3: 68: FF7508 push [ebp+8]
71: 8B45FC move eax, [ebp-4]
74: E879F757FF call 209D4E42 ; #<function 209D4E42>
79: EBD4 jmp L1
L4: 81: 894508 move [ebp+8], eax
84: B87C004521 move eax, 2145007C ; 3.14
89: C9 leave
90: E991F757FF jmp 209D4E6A ; #<function 209D4E6A>
95: 90 nop
96: 90 nop
97: 90 nop
Многовато, если не сказать грубее... Динамическая типизация во всей красе. Однако, все в наших руках. Добавляем такое магическое заклинание:
(defun f (a b)
(declare (type double-float a b))
(+ (* a b) 3.14))
Реализация функции становится гораздо приятнее:
20695052:
0: 3B25BC150020 cmp esp, [200015BC] ; T
6: 7628 jbe L1
8: 80FD02 cmpb ch, 2
11: 7523 jne L1
13: 55 push ebp
14: 89E5 move ebp, esp
16: 50 push eax
17: FF7508 push [ebp+8]
20: B502 moveb ch, 2
22: 8B45FC move eax, [ebp-4]
25: FF1598BA2720 call [2027BA98] ; SYSTEM::*$DOUBLE$DOUBLE
31: 894508 move [ebp+8], eax
34: B502 moveb ch, 2
36: B8B4D76720 move eax, 2067D7B4 ; 3.14
41: C9 leave
42: FF25D8C52720 jmp [2027C5D8] ; SYSTEM::+$DOUBLE$DOUBLE
L1: 48: E8DB269BFF call 20047762 ; #<function 20047762>
53: 90 nop
Уже гораздо лучше, но можно добиться большего:
(defun f (a b)
(declare (optimize (speed 3) (safety 0) (debug 0) (space 0) (float 0)))
(declare (type double-float a b))
(+ (* a b) 3.14))
Дает нам
20673522:
0: 55 push ebp
1: 89E5 move ebp, esp
3: 83EC14 sub esp, 14
6: C7042445140000 move [esp], 1445
13: 8B7D08 move edi, [ebp+8]
16: DD4704 fldl [edi+4]
19: DD4004 fldl [eax+4]
22: DEC9 fmulp st(1), st
24: DD0590786820 fldl [20687890] ; 3.14
30: DEC1 faddp st(1), st
32: FF75F0 push [ebp-10]
35: FF75EC push [ebp-14]
38: 8B75F4 move esi, [ebp-C]
41: 8975EC move [ebp-14], esi
44: 8B75F8 move esi, [ebp-8]
47: 8975F0 move [ebp-10], esi
50: 8B75FC move esi, [ebp-4]
53: 8975F4 move [ebp-C], esi
56: 8B7500 move esi, [ebp]
59: 8975F8 move [ebp-8], esi
62: 83ED08 sub ebp, 8
65: 8B750C move esi, [ebp+C]
68: 897504 move [ebp+4], esi
71: DD5D0C fstpl [ebp+C]
74: C74508450C0000 move [ebp+8], C45
81: B501 moveb ch, 1
83: C9 leave
84: FF2510671120 jmp [20116710] ; SYSTEM::RAW-FAST-BOX-DOUBLE
Собственно тело функции --- чистый машинный код, без каких-бы то ни было накладных расходов (той самой "подстилки", которой нас пугают), что и требовалось. Пролог и эпилог по-прежнему великоваты, но не исключено, что добавлением дополнительных деклараций можно справиться и с ними.
А сейчас --- раскроем страшную тайну. :-) Надежность такого кода, вообще говоря, не выше аналогичного сишного. Но (вот она, тайна!) никто не заставляет писать его вручную. Средствами Лиспа определяется соотв. DSL, который возьмет на себя генерацию всех этих объявлений, и в который никто не мешает включить статическую типизацию, хоть с выводом типов в стиле Haskell (что может оказаться и не нужным --- хорошие Лисп-трансляторы уже включают в себя элементы type inference), хоть с явной типизацией, как в Обероне. Кроме обобщенного контроля типов, которого уже достаточно, чтобы вернуть потерянную надежность, можно добавить столько domain-specific проверок, сколько мы сможем себе вообразить. Добавим к этому возможность domain-specific оптимизаций и адаптивность --- возможность перекомпиляции кода прямо во время его исполнения.
Ответ на
»сообщение 2553« (Руслан Богатырев)
___________________________
Где же начнется "просадка"? Уж не на работе ли с динамической памятью?
Да, это одна из известных потенциальных угроз производительности, которую, если уж так прижмет, приходится обходить весьма жесткими методами (в частности, ценой может стать отказ от ФП со всеми его преимуществами).
Так может, разговоры о преимуществе на каких-то тестах -- это просто попытка самоуспокоения, а не поиск истины?
ИМХО, любые тесты производительности, кроме профилирования реальных программ в реальных условиях их эксплуатации --- это самообман.