Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

Джоэл Спольски о функциональном программировании

От автора сайта. Ниже представлена краткая выжимка статьи Джоэла Спольски «А ваш язык программирования так может?». Краткая, но достаточная, чтобы понять главные идеи. Статья написана в 2006 году и какие-то сведения уже устарели. Например, Спольски тогда не мог предвидеть, что C++ и другие языки будет активно пополняться функциональными возможностями. Однако, это не умаляет важности мыслей и идей Спольски. Хотя не всё так гладко и радужно, как он рисует, но к этому ещё вернёмся в послесловии.

Джоэл Спольки о функциональном программировании

Си позволяет объявить тип «функция». Можно заранее объявить некую функцию, а затем передать её в другую функцию, один (или несколько) из параметров которой имеет тип «функция». В эту другую функцию передаётся указатель на объявленную ранее функцию. Безымянные же функции можно не объявлять заранее, а объявить прямо по месту употребления.

f1 (param1, param2, f2(x) {return x*x;});		// Javascript

Проделывание чего-нибудь с каждым элементом массива достаточно распространено, и вы можете написать функцию, которая делает это для вас:

   function map(fn, a)
   {
       for (i = 0; i < a.length; i++)
       {
           a[i] = fn(a[i]);
       }
   }

Теперь вы можете переписать предыдущий код:

   map( function(x){return x*2;}, a );
   map( alert, a );
Другая распространенная операция с массивами — как-нибудь объединить все его значения.
   function sum(a)
   {
       var s = 0;
       for (i = 0; i < a.length; i++)
           s += a[i];
       return s;
   }

   function join(a)
   {
       var s = "";
       for (i = 0; i < a.length; i++)
           s += a[i];
       return s;
   }

   alert(sum([1,2,3]));
   alert(join(["a","b","c"]));

sum и join выглядят так одинаково, что вы можете захотеть абстрагировать их сущность в общую функцию, которая объединяет элементы массива в одно значение:

   function reduce(fn, a, init)
   {
       var s = init;
       for (i = 0; i < a.length; i++)
           s = fn( s, a[i] );
       return s;
   }

   function sum(a)
   {
       return reduce( function(a, b){ return a + b; }, 
                      a, 0 );
   }
    
   function join(a)
   {
       return reduce( function(a, b){ return a + b; }, 
                      a, "" );
   }

Во многих старых языках программирования просто нет способа проделать этот трюк. Другие языки позволят вам сделать это, но достаточно сложно (например, в Си есть указатели на функции, но вы должны объявить и определить функцию где-либо в другом месте). Объектно-ориентированные языки программирования не вполне уверены, что вам следует разрешить делать что-нибудь с функциями.

Java требует от вас создать отдельный объект с одним методом, так называемый функтор, если вы хотите представить функцию как объект первого класса (first class object, т. е. сущности языка программирования, которые можно использовать без ограничений при хранении в переменных, хранении в более сложных структурах, для передачи в функции в качестве аргумента, и возвращения в качестве результата). Добавьте к этому факт, что множество ОО-языков вынуждают вас создавать отдельный файл для каждого класса, и это делает код неуклюжим очень быстро. Если ваш язык программирования требует от вас использования функторов, вы не получите всех преимуществ современной среды программирования. Посмотрите, сможете ли вы получить ваши деньги обратно.

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

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

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

Таким образом сейчас, например, написание какого-либо по-настоящему быстрого кода для поиска во всём содержимом Интернета просто сводится к вызову функции map с простой функцией поиска строки в качестве аргумента.

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

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

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

Без понимания функционального программирования вы не сможете придумать MapReduce — алгоритма, который делает Google столь хорошо масштабируемым. Термины Map и Reduce пришли из Lisp и функционального программирования. MapReduce понятен любому, кто помнит со скамьи вуза, что истинно функциональные программы не имеют побочных эффектов и поэтому легко распараллеливаемы. Очень показателен тот факт, что в Google изобрели MapReduce, а в Microsoft нет, и это говорит кое-что о том, почему Microsoft до сих пор играет в догонялки, пытаясь заставить работать основные функции поисковой машины, в то время как в Google перешли к следующей проблеме: построению Skynet величайшего в мире параллельного суперкомпьютера. Я не думаю, что Microsoft действительно понимает, насколько они отстали на этом пути.

OK. Надеюсь, я вас уже убедил, что языки программирования, где функции — объекты первого класса, предоставляют вам больше возможностей для абстрагирования. Это значит, что ваш код меньше, надежнее, лучше для повторного использования и более масштабируемый. Множество приложений Google используют MapReduce и они все выигрывают, когда кто-нибудь оптимизирует его или исправляет ошибку.

А сейчас я буду немного непоследовательным и буду утверждать, что наиболее эффективны те среды программированию, которые позволяют работать на разных уровнях абстракции. Старый недобрый FORTRAN даже не позволял создавать функции. Си содержит указатели на функции, но они уродливы, не могут быть безымянными и их реализация должна быть написана где-то в месте отличном от того, где они используются. Java заставляет использовать функторы, которые ещё уродливее. Как заметил Steve Yegge, Java — это Королевство Существительных.

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

Опубликовано: 2022.01.02, последняя правка: 2023.03.05    01:06

ОценитеОценки посетителей
   ███████████ 3 (25%)
   ██████████████ 4 (33.3%)
   ███████████ 3 (25%)
   ███████ 2 (16.6%)

Добавить свой отзыв

Написать автору можно на электронную почту
mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Содержание

Каким должен быть язык программирования?

Анализ и критика

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

●  О превращении кибернетики в шаманство

●  Про лебедей, раков и щук

●  О замысле и воплощении

●  О русском ассемблере

●  Арифметика синтаксиса-3

●  Концепция владения в Rust на примерах

●●  Концепция владения в Rust на примерах, часть 2

●●  Концепция владения в Rust на примерах, часть 3

●  Суть побочных эффектов в чисто функциональных языках

●  О неулучшаемой архитектуре процессоров

●  Двадцать тысяч строк кода, которые потрясут мир?

●  Почему владение/заимствование в Rust такое сложное?

●  Масштабируемые архитектуры программ

●  О создании языков

●●  Джоэл Спольски о функциональном программировании

●  Почему Хаскелл так мало используется в отрасли?

●  Программирование исчезнет. Будет дрессировка нейронных сетей

●  О глупости «программирования на естественном языке»

●  Десятка худших фич C#

●  Бесплатный софт в мышеловке

●  Исповедь правового нигилиста

●  ЕС ЭВМ — это измена, трусость и обман?

●  Русской операционной системой должна стать ReactOS

●  Почему обречён язык Форт

●  Программирование без программистов — это медицина без врачей

●  Электроника без электронщиков

●  Программисты-профессионалы и программирующие инженеры

●  Статьи Дмитрия Караваева

●●  Идеальный транслятор

●●  В защиту PL/1

●●  К вопросу о совершенствовании языка программирования

●●  Опыт самостоятельного развития средства программирования в РКК «Энергия»

●●  О реализации метода оптимизации при компиляции

●●  О реализации метода распределения регистров при компиляции

●●  О распределении памяти при выполнении теста Кнута

●●  Опыты со стеком или «чемпионат по выполнению теста Кнута»

●●  О размещении переменных в стеке

●●  Сколько проходов должно быть у транслятора?

●●  Чтение лексем

●●  Экстракоды при синтезе программ

●●  Об исключенных командах или за что «списали» инструкцию INTO?

●●  Типы в инженерных задачах

●●  Непрерывное компилирование

●●  Об одной реализации специализированных операторов ввода-вывода

●●  Особенности реализации структурной обработки исключений в Win64

●●  О русском языке в программировании

●●  Формула расчета точности для умножения

●●  Права доступа к переменным

●●  Заметки о выходе из функции без значения и зеркальности get и put

●●  Модификация исполняемого кода как способ реализации массивов с изменяемыми границами

●●  Ошибка при отсутствии выполняемых действий

●●  О PL/1 и почему в нём не зарезервированы ключевые слова

●●  Не поминайте всуе PL/1

●●  Скорость в попугаях

●●  Крах операции «Инкогнито»

●●  Предопределённый результат

●●  Поддержка профилирования кода программы на низком уровне

●●  К вопросу о парадигмах

●  Следующие 7000 языков программирования

●●  Что нового с 1966 года?

●●  Наблюдаемая эволюция языка программирования

●●  Ряд важных языков в 2017 году

●●  Слоны в комнате

●●  Следующие 7000 языков программирования: заключение

Компьютерный юмор

Новости и прочее




Последние отзывы

2024/12/07 20:54 ••• Клихальт
Переключатель

2024/12/06 18:44 ••• Анкнав
Русский язык и программирование

2024/12/01 00:00 ••• alextretyak
Продолжение цикла и выход из него

2024/11/29 23:08 ••• Вежливый Лис
О русском ассемблере

2024/11/26 23:53 ••• Бурановский дедушка
ЕС ЭВМ — это измена, трусость и обман?

2024/11/25 18:31 ••• Деньги на WWWетер
Ресурсы, посвящённые созданию языков программирования и компиляторов

2024/11/12 20:24 ••• Вежливый Лис
Правила языка: строки, комментарии

2024/11/12 13:10 ••• Вежливый Лис
Новости и прочее

2024/11/12 00:32 ••• Автор сайта
Оценка надёжности функции с несколькими реализациями

2024/11/06 02:50 ••• Иван
Энтузиасты-разработчики компиляторов и их проекты

2024/11/05 23:51 ••• Борис К.
Изменение приоритетов операций

2024/11/05 23:38 ••• Борис К.
Шестнадцатиричные и двоичные константы

2024/11/01 12:11 ••• ИванАс
Русской операционной системой должна стать ReactOS