суббота, 2 ноября 2013 г.

О функциональных и императивных языках. О функциональном и императивном подходах

Скажем так "вводная" была тут - http://18delphi.blogspot.com/2013/11/blog-post_4093.html

Сегодня вышел спор с одним из коллег по цеху.

О том, кто и как понимает функциональное и императивное программирование и их принципы. И "что чем является".

Дискуссия местами велась в стиле "Ленин vs Мартов", ну или "Маркс vs Каутский". Ну или "уроки Октября" и "об уроках Октября".

И ОБЕ стороны - "были хороши".

В итоге наговорили друг другу некоторое количество "не очень приятных вещей".

Ну и разошлись - "каждый довольный собой".

Если бы была возможность "расстрелять оппонента", то "расстреляли бы". Ну или на худой конец - "сослали бы в Алма-Ату".

Что хочу сказать?

Ну я ЛИЧНО собой-то не очень "остался доволен".

И видимо заслуженно получил упрёки в том, что "я не слушаю", да и вообще - "никто не просил помогать".

Наверное.

Посему - хочу всё же - "прояснить ситуацию".

И ОБЪЯСНИТЬ - "своё виденье" вопроса. И ЧТО ИМЕННО я пытался донести.

НЕ ПОТОМУ, что "я прав".

В данном вопросе - "правых нет". По-моему.

Есть лишь - "личные трактовки".

Хотя может быть "моя личная трактовка" - ДЕЙСТВИТЕЛЬНО - "ЛИЧНАЯ" и сторонников у меня нет.

ДОПУСКАЮ.

Мне это - НЕ НОВО.

Теперь - к делу.

Суть вопроса была примерно в следующем - "какие языки считать функциональными и почему, а какие не считать".

Мой оппонент приводил следующее определение:
"Функциона́льное программи́рование — раздел дискретной математики и парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании).
Противопоставляется парадигме императивного программирования, которая описывает процесс вычислений как последовательное изменение состояний (в значении, подобном таковому в теории автоматов). При необходимости, в функциональном программировании вся совокупность последовательных состояний вычислительного процесса представляется явным образом, например как список.
Функциональное программирование предполагает обходиться вычислением результатов функций от исходных данных и результатов других функций, и не предполагает явного хранения состояния программы. Соответственно, не предполагает оно и изменяемость этого состояния (в отличие от императивного, где одной из базовых концепций является переменная, хранящая своё значение и позволяющая менять его по мере выполнения алгоритма)."
взято отсюда - http://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5

И он НАСТАИВАЛ на своей точке зрения, что мол "раз нет состояний" - значит ФУНКЦИОНАЛЬНОЕ программирование.

Ну и с эти не поспоришь в общем-то. Да я с ЭТИМ И НЕ СПОРИЛ.

Наверное просто был НЕ ТАК ПОНЯТ.

Но!

Я НЕ ЗРЯ написал про "теорию и ПРАКТИКУ ПРИМЕНЕНИЯ".

Собственно какую мысль я пытался донести до своего оппонента?

А вот какую:

"нет состояний" и "нет императивных конструкций" - это конечно - ХОРОШО.

Но! ЗАЧЕМ ЭТО? Суть вопроса в чём?

Зачем отказываться от "состояний" и "императивных конструкций".

Зачем "усложнять себе жизнь". Спрашивал я.

Только ради "чистоты арийской расы"? Только ради "применения чистого функционального программирования"?

Я не спорю, что ЛЮБОЙ язык без состояний и императивных конструкций является ФУНКЦИОНАЛЬНЫМ. (Хотя - как же "монады"? Это разве не "императивная надстройка? Сейчас меня побьют ногами...)

Но! Вот давайте возьмём Паскаль. На нём можно программировать в ФУНКЦИОНАЛЬНОМ СТИЛЕ (согласно ПРИВЕДЁННОМУ ВЫШЕ ОПРЕДЕЛЕНИЮ)?

Можно?

Ну в общем - в большинстве случаев - МОЖНО.
НУЖНО ЛИ? Ну наверное - В БОЛЬШИНСТВЕ случаев - НУЖНО.

Зачем?

Для того чтобы повысить стабильность кода.

Согласен.

Является ли Паскаль после этого "функциональным языком"?

Я - НЕ ЗНАЮ.

Хотелось бы услышать мнение моего оппонента.

Что же говорил я?
 В чём СУТЬ разногласий?

Я говорил примерно следующее - "ТЕОРИЯ - это ХОРОШО. Отсутствие состояний и "императивных конструкций" - ЗДОРОВО. НО ЗАЧЕМ?

Давайте "погрузимся в байты". Говорил я.

Давайте прочитаем СЛЕДУЮЩИЙ АБЗАЦ.

Вот он:
"На практике отличие математической функции от понятия «функции» в императивном программировании заключается в том, что императивные функции могут опираться не только на аргументы, но и на состояние внешних по отношению к функции переменных, а также иметь побочные эффекты и менять состояние внешних переменных. Таким образом, в императивном программировании при вызове одной и той же функции с одинаковыми параметрами, но на разных этапах выполнения алгоритма, можно получить разные данные на выходе из-за влияния на функцию состояния переменных. А в функциональном языке при вызове функции с одними и теми же аргументами мы всегда получим одинаковый результат: выходные данные зависят только от входных. Это позволяет средам выполнения программ на функциональных языках кешировать результаты функций и вызывать их в порядке, не определяемом алгоритмом и распараллеливать их без каких-либо дополнительных действий со стороны программиста (см.ниже Чистые функции)"

Отсутствие СОСТОЯНИЙ, т.е. ДЕТЕРМИНИРОВАННОСТЬ - она КОНЕЧНО ВЕДЁТ к стабильности кода "самого по себе".

Представим язык "без глобальных переменных" и всё - СТАНЕТ ЯСНО.

И ТУТ - мой оппонент - ПРАВ!

БЕЗ глобальных переменных - СТАБИЛЬНОСТЬ повышается.

Но! Спрашивал я.

Не МНОГИМ ли мы жертвуем ОТКАЗЫВАЯСЬ от "состояний" и "императивных конструкций"?

ЗАРАДИ ЧЕГО такие жертвы?

Не РАДИ ли - кешируемости, параллелизма и оптимизации порядка выполнения?

Не ЭТО ли должно быть ПОСТАВЛЕНО ВО ГЛАВУ УГЛА?

Т.е. "отсутствие состояний" и "императивных конструкций" - это ОПРЕДЕЛЕНИЕ. Но! ОПРЕДЕЛЕНИЕ - определят ли ПАРАДИГМУ и СУТЬ функциональных языков.

По-моему - НЕТ.

По-моему - ГЛАВНОЕ как раз, что "мы жертвуем состояниями и императиивностью" ВО ИМЯ "кешируемости, параллелизма и оптимизации порядка выполнения (в частности хвостовой рекурсии").

ВО ИМЯ возможности БОЛЕЕ ЭФФЕКТИВНЫХ вычислений.

А ИНАЧЕ - ЗАЧЕМ ВСЯ эта функциональная парадигма? Чем она ЛУЧШЕ ИМПЕРАТИВНОЙ?

Глобальные переменные - ОТЛОЖИМ В СТОРОНУ. Тут - Я СОГЛАСИЛСЯ. Они - УХУДШАЮТ СТАБИЛЬНОСТЬ.

Если следовать "чистой теории", то "кастрированный" Паскаль - ведь является функциональным языком?

ЯВЛЯЕТСЯ? Я - не знаю. Вопрос к моему оппоненту. Мне кажется, что - ЯВЛЯЕТСЯ.

Могу ошибаться.

Многие ли люди согласятся программировать на подобном языке? Получат ли они выгоду?

Мне кажется, что - нет.

Могу ошибаться.

Так "функциональность языка" это ли лишь "отсутствие состояний и императивных конструкций". Или всё же функциональность "подразумевает" - "кешируемость, параллелизм и оптимизацию вычислений"?

Не знаю.

Для меня - ВОПРОС ОТКРЫТ.

За данный вопрос я получил "ярлык" - "ты придумываешь свои определения".

Может быть.

Только - НЕ ПРИДУМЫВАЮ, а ПЫТАЮСЬ ПОНЯТЬ.

Ну и ещё я получил упрёк - "ты всюду пихаешь свой Форт и форт-машину".

Опять же - НЕ "пихаю". А ПЫТАЮСЬ поделиться.

Я - НЕ НАВЯЗЫВАЮ НИЧЕГО.

Я лишь пишу - О ЧЁМ ЗНАЮ.

Когда мне говорят - "никто не просил помогать" - ну грустно, чего уж...

НЕ просил - простите. Больше не буду.

Я может быть только "неправильно выражаюсь". Форт и форт-машина - всё же РАЗНЫЕ вещи, хотя и связанные.

Точнее даже - "стековая-машина".

Ну практически все компиляторы - ПОСТРОЕНЫ на ней. Ну читаем например статью Болье - "методы построения компиляторов".

И когда я писал про форт-машину - я НИКОИМ ОБРАЗОМ не имел в виду - "разбор токенов по пробелам" или "обратную польскую запись". Эти ЧАСТНОСТИ - УЖЕ сильно выше. В реализации. Я имел в виду лишь стековую-машину и "словарь".

То и другое, так или иначе - присутствует во всех современных компиляторах.

Иногда словарь называется "деревом разбора".

(Или я опять "придумываю собственные определения"?)

Пи-код, байт-код и прочее и прочее - всё из одной и той же области.

И если я ПРЕДЛАГАЛ, для РЕАЛИЗАЦИИ функционального языка использовать форт-машину (стековую-машину) это НЕ ЗНАЧИТ, что я предлагал ОТКАЗАТЬСЯ от ФУНКЦИОНАЛЬНОСТИ. Просто мне КАЗАЛОСЬ, что для КОНКРЕТНОЙ реализации - она очень даже МОЖЕТ ПОДОЙТИ.

http://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D1%82_(%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)

Испытал влияние:
http://ru.wikipedia.org/wiki/Erlang
"Erlang [ˈɜːlæŋ] — Э́рланг — функциональный язык программирования с динамической типизацией, предназначенный для создания распределённых вычислительных систем. Разработан и поддерживается компанией Ericsson. Язык включает в себя средства порождения параллельных процессов и их коммуникации с помощью асинхронных сообщений и в соответствии с моделью акторов. Программа транслируется в байт-код, исполняемый виртуальной машиной, что обеспечивает переносимость. Кратко формулу языка можно выразить как Erlang = функциональный язык + процессы."

http://ru.wikipedia.org/wiki/Haskell
"
  • YHC (York Haskell Compiler) — форк nhc98, ставящий целью быть более переносимым и эффективным, поддерживает отладчик Hat; генерирует промежуточный байт-код, который можно использовать для генерации кода на других языках программирования"

Всё - взаимосвязано..

Посему резюме:
1. ДОСТАТОЧНЫМ ли условием ЯВЛЯЕТСЯ для "функциональности" языка - ОТСУТСТВИЕ "состояний и императивных конструкций".
2. Можно ли строить "функциональные" языки - на стековой-машине.

-- эти вопросы я оставляю ОТКРЫТЫМИ.

Для своего оппонент скажу лишь. ИМЕННО это я и имел в виду, когда всё писал.
НИ БОЛЬШЕ, НИ меньше.

И никого "поучать" и в мыслях - НЕ БЫЛО.

16 комментариев:

  1. >Если следовать "чистой теории", то "кастрированный" Паскаль - ведь является функциональным языком?

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

    >Так "функциональность языка" это ли лишь "отсутствие состояний и императивных конструкций". Или всё же функциональность "подразумевает" - "кешируемость, параллелизм и оптимизацию вычислений"?

    Ключевая разница в иных терминах. А вышеописанные оптимизации - лишь следствия другого способа описания.

    >Не МНОГИМ ли мы жертвуем ОТКАЗЫВАЯСЬ от "состояний" и "императивных конструкций"?

    На самом деле мы многое приобретаем, и "забываем" много лишнего.

    >Можно ли строить "функциональные" языки - на стековой-машине.

    Если речь только об стековой нотации(ОПН) - можно. Если речь именно о машине(которая подразумевает последовательное изменение состояний), как части терминологии языка - нет.

    ОтветитьУдалить
  2. Я тоже хочу с кем-нибудь поссориться (хабр утомил своим отрицательным интеллектуальным уровнем оппонетов. гуляя там я постоянно спотыкаюсь и проваливаюсь в ямы).


    1. К чему все эти визги с функциональным программированием? Друг всех программистов Страуструп наврал, что-де значимость функциональных языков переоценена, если смотреть на применимость резултатов исследования? Когда я смотрел примеры кода г-на Сошникова (Microsoft, апологет F#), то что-то реально интересное смотрелось уже как C# с расстояния вытянутой руки. Ландшафт кода, понимаете ли.

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

    3. Ну и чисто совсем уже быдлячий вопрос. Функциональное программирование - это об чём? Желание математиков придумать свой "птичий" язык для рыб (программистов)? Очень много споров на тему что есть и что не функциональное программирование. Но это похоже на споры о качестве "эльфийского выдуманного языка".

    >>"нет состояний" и "нет императивных конструкций" - это конечно - ХОРОШО.
    Прям крошка сын к отцу пришёл. :)

    Мыть руки - хорошо.
    Слушать босса - хорошо.
    Курить - плохо.
    Императивное программирование - плохо, но хорошо.
    Функциональное программирование - хорошо.

    Можно от обоих оппонентов чего-то менее декларативного? Чай не декрет Ленина :)

    ОтветитьУдалить
  3. К чему "визги"? Вот к чему - http://local.joelonsoftware.com/wiki/%D0%90_%D0%B2%D0%B0%D1%88_%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D1%82%D0%B0%D0%BA_%D0%BC%D0%BE%D0%B6%D0%B5%D1%82%3F

    ОтветитьУдалить
  4. Зачем детерминированность? На МОЙ ВЗГЛЯД - для кеширования и параллелизма.

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

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

    Запрет же глобального изменяемого состояния всего лишь делит те же функциональные языки на две группы -- чистые (как Хаскелл или Clean) и грязные (Окамл/F#).

    ОтветитьУдалить
    Ответы
    1. "Позволяет ли Паскаль функциям возвращать другие функции? Насколько я знаю -- нет. "

      Здрасте... Приехали...

      type SomeFunction = function;
      type SomeFunctionEvent = function of object;

      Удалить
    2. В Паскале нет лямбд, вот что важно.

      Удалить
  7. "В Паскале нет лямбд, вот что важно.
    1. Ты сам говорил, что "это не самое важное".
    2. Есть анонимные функции. Ну если мы про ембаркадеру.

    ОтветитьУдалить
    Ответы
    1. 1. Это как раз чуть ли не самое важное. Функции должны быть first-class citizen.
      2. Так-то да, эмбаркадеровская делфи куда ближе к ФП, чем обычный паскаль

      Можно еще вспомнить про то, что в функциональных языках функции применяются (apply), а в императивных - вызываются (call). Термины - это довольно важно.

      Удалить
    2. "Можно еще вспомнить про то, что в функциональных языках функции применяются (apply), а в императивных - вызываются (call). Термины - это довольно важно."
      -- поясни, что ты имеешь в виду :-) Я - не в курсе..

      Удалить
    3. Ну.. в отсутствии "необходимости доступа к локальным переменным", которых в ФЯ - просто нету - необходимость лямбд и их "красота" в ФЯ - для меня практически потеряла смысл... Для меня это НЕ БОЛЕЕ чем, "ещё один способ" определения новой функции, через существующую.. Я пытался это донести, но похоже - не донёс...

      А вот "анонимные функции" в ИМПЕРАТИВЕ - ОЧЕНЬ даже важны. Они имеют доступ к "контексту".. Которого в ФЯ - нет... ПО ОПРЕДЕЛЕНИЮ, которое ты сам и цитировал...

      Наверное я конечно что-то недопонимаю.. И наверное опять "не о том рассуждаю"... Но я рассуждаю как "практик", как "инженер" если угодно...

      Мне кстати написали тут - "читайте лучше про Cobol, а не про Haskell"...

      ;-)

      Иронию и "намёк" - понял..

      Перечитал :-)

      Да.. Cobol мне нравится гораздо больше, чем ФЯ...

      Кобол он СИЛЬНО БЛИЖЕ к "тестированию по-русски" :-)

      Удалить
    4. "А вот "анонимные функции" в ИМПЕРАТИВЕ - ОЧЕНЬ даже важны. Они имеют доступ к "контексту".. Которого в ФЯ - нет... ПО ОПРЕДЕЛЕНИЮ, которое ты сам и цитировал..."

      С действительностью это не имеет ничего общего, но ладно, пусть будет так как ты хочешь.
      Было бы интересно - реально бы уже давно разобрался с темой. Даже в моем "минилиспе" были и лямбды, и контекст. Куда уж понятнее, не знаю.

      Удалить
    5. > А вот "анонимные функции" в ИМПЕРАТИВЕ - ОЧЕНЬ даже важны. Они имеют доступ к "контексту".. Которого в ФЯ - нет... ПО ОПРЕДЕЛЕНИЮ, которое ты сам и цитировал...

      Что Вы имеете в виду под контекстом? Внутреннее состояние процедуры?
      Так в ФЯ оно тоже есть, вот только хранится в параметрах функций, а внутри самх функций не изменяется.

      Удалить
    6. "вот только хранится в параметрах функций, а внутри самх функций не изменяется."
      -- конечно, и именно поэтому лямбды там не так важны как в императиве.

      Удалить
  8. > конечно, и именно поэтому лямбды там не так важны как в императиве.

    У меня складывается впечатление, что Вы просто троллите меня и других...

    Как раз в императивных языках редко обращают внимание на лямбда-функции, это в последние годы растущий интерес к ФП заставляет добавлять лямбды в распространённые языки.

    Если под "там" Вы имели в виду функциольнальное программирование, основанное на лямбда-исчислении, то мне остаётся лишь развести руками, поскольку ФП без лямбд -- не мыслим.

    ЗЫ. Ну, в комбинаторной логике лямбды и правда не нужны, но писать программы в комбинаторном стиле в большинстве случаев, имхо, -- извращение.

    ОтветитьУдалить