Я работаю в течение четырех лет в функциональном программировании, с тех пор как я впервые начал работать с LINQ. Недавно я написал некоторый чистый функциональный код на C # и из первых рук заметил, что я читал о функциональных программах - что после компиляции они становятся правильными.
Я пытался понять, почему это так, но мне это не удалось.
Одно из предположений состоит в том, что при применении принципов ОО у вас есть «уровень абстракции», которого нет в функциональных программах, и этот уровень абстракции позволяет сделать контракты между объектами правильными, а реализация - неправильной.
Задумывался ли кто-нибудь об этом и придумал ли абстрактную причину корреляции между успехом компиляции и корректностью программы в функциональном программировании?
источник
final
по всему).Ответы:
Я могу написать этот ответ как кто-то, кто много доказывает, поэтому для меня правильность - это не только то, что работает, но то, что работает и легко доказать.
Во многих смыслах функциональное программирование является более строгим, чем императивное программирование. В конце концов, ничто не мешает вам никогда не мутировать переменную в C! На самом деле, большинство функций в языках FP просто говорить о нескольких основных функциях. Все это в значительной степени сводится к лямбдам, применению функций и сопоставлению с образцом!
Однако, поскольку мы заранее заплатили за пайпера, у нас намного меньше проблем, и у нас гораздо меньше вариантов того, как все может пойти не так. Если вы поклонник 1984 года, свобода - это действительно рабство! Используя 101 различную хитрость для программы, мы должны рассуждать о вещах, как будто любая из этих 101 вещи может произойти! Это действительно сложно сделать, как оказалось :)
Если вы начнете с безопасных ножниц вместо меча, бег будет умеренно менее опасным.
Теперь посмотрим на ваш вопрос: как все это вписывается в «это компилируется и работает!» явления. Я думаю, что большая часть этого является той же самой причиной, почему легко доказать код! В конце концов, когда вы пишете программное обеспечение, вы создаете неформальное доказательство его правильности. Из-за этого то, что покрыто вашими естественными доказательствами ручной обработки и собственным представлением компиляторов, является верным (проверка типов).
По мере добавления функций и сложных взаимодействий между ними увеличивается то, что не проверяется системой типов. Тем не менее, ваша способность создавать неформальные доказательства не улучшается! Это означает, что есть еще что-то, что может пройти через ваш первоначальный осмотр и должно быть обнаружено позже.
источник
Изменчивое состояние.
Компиляторы проверяют вещи статически. Они гарантируют, что ваша программа правильно сформирована, а система типов обеспечивает механизм, который позволяет убедиться, что в нужных местах разрешены правильные значения. Система типов также пытается гарантировать, что правильная семантика разрешена в правильных местах.
Как только ваша программа вводит состояние, последнее ограничение становится менее полезным. Вам нужно не только беспокоиться о правильных значениях в правильных местах, но также необходимо учитывать изменение этих значений в произвольных точках вашей программы. Вы должны учитывать семантику вашего кода, изменяющуюся вместе с этим состоянием.
Если вы хорошо выполняете функциональное программирование, нет изменяемого состояния (или очень мало).
Здесь есть некоторые споры о причинности - если программы без состояния работают после компиляции чаще, потому что компилятор может отлавливать больше ошибок, или если программы без состояния работают после компиляции чаще, потому что этот стиль программирования производит меньше ошибок.
Скорее всего, это и то и другое в моем опыте.
источник
Проще говоря, ограничения означают, что правильных способов объединить все меньше, а первоклассные функции облегчают выделение таких вещей, как структуры цикла. Возьмем цикл из этого ответа , например:
Это единственный безопасный и обязательный способ в Java для удаления элемента из коллекции во время его итерации. Есть много способов, которые выглядят очень близко, но они ошибочны. Люди, не знающие об этом методе, иногда используют запутанные способы избежать проблемы, например, вместо этого перебирая копию.
Это не очень сложно сделать таким универсальным, поэтому он будет работать не только с коллекциями
Strings
, но без первоклассных функций вы не можете заменить предикат (условие внутриif
), поэтому этот код имеет тенденцию копироваться и вставляться и немного изменен.Комбинируйте первоклассные функции, которые дают вам возможность передавать предикат в качестве параметра, с ограничением неизменности, которое делает его очень раздражающим, если вы этого не делаете, и вы придумываете простые строительные блоки, как
filter
, например , в этом коде Scala это делает то же самое:Теперь подумайте о том, что система типов проверяет для вас во время компиляции в случае Scala, но эти проверки также выполняются динамическими системами типов при первом запуске:
list
должен быть какой-то тип, который поддерживаетfilter
метод, а именно коллекция.list
должны иметьisEmpty
метод, который возвращает логическое значение.После того, как эти вещи были проверены, какие другие способы остаются для программиста, чтобы испортить? Я случайно забыл
!
, что вызвало крайне очевидный сбой тестового случая. Это в значительной степени единственная доступная ошибка, которую я могу сделать, и я сделал это только потому, что непосредственно переводил код, проверенный на обратное условие.Этот шаблон повторяется снова и снова. Первоклассные функции позволяют вам преобразовывать вещи в небольшие повторно используемые утилиты с точной семантикой, ограничения, такие как неизменность, дают вам стимул для этого, а проверка типов этих утилит оставляет мало места, чтобы их испортить.
Конечно, все это зависит от того, знает ли программист, что упрощающая функция, подобная этой,
filter
уже существует, и может ли ее найти или осознавать преимущества ее создания самостоятельно. Попробуйте реализовать это везде, используя только хвостовую рекурсию, и вы снова окажетесь в той же лодке сложности, что и императивная версия, только хуже. То, что вы можете написать это очень просто, не означает, что простая версия очевидна.источник
Я не думаю, что есть существенная корреляция между компиляцией функционального программирования и корректностью времени выполнения. Может быть некоторая корреляция между статически типизированной компиляцией и корректностью времени выполнения, так как, по крайней мере, у вас могут быть правильные типы, если вы не читаете.
Как вы описываете, аспект языка программирования, который может как-то соотносить успешную компиляцию с корректностью типов во время выполнения, - это статическая типизация, и даже тогда, только если вы не ослабляете средство проверки типов с помощью приведений, которые могут быть утверждены только во время выполнения (в средах с строго типизированные значения или места, например, Java или .Net) или их нет вообще (в средах, где информация о типе теряется или при слабой типизации, например, C и C ++).
Тем не менее, функциональное программирование само по себе может помочь другими способами, такими как избегание общих данных и изменяемого состояния.
Оба аспекта вместе могут иметь существенную корреляцию в правильности, но вы должны знать, что отсутствие ошибок компиляции и времени выполнения ничего не говорит, строго говоря, о корректности в более широком смысле, поскольку в программе выполняется то, что она должна делать, и быстро терпит неудачу из-за неверного ввода или неконтролируемого сбоя во время выполнения. Для этого вам нужны бизнес-правила, требования, варианты использования, утверждения, модульные тесты, интеграционные тесты и т. Д. В конце концов, по крайней мере, на мой взгляд, они обеспечивают гораздо большую уверенность, чем функциональное программирование, статическая типизация или и то, и другое.
источник
Пояснения для менеджеров:
Функциональная программа похожа на одну большую машину, где все соединено, трубки, кабели. [Машина]
Процедурная программа похожа на здание с комнатами, в которых находится небольшая машина, в которой хранятся частичные продукты в мусорных баках, а частично - продукты из других источников. [Завод]
Поэтому, когда функциональная машина уже объединяется: она обязательно что-то производит. Если процедурный комплекс запускается, вы могли наблюдать конкретные эффекты, вводить хаос, а не гарантировать его функционирование. Даже если у вас есть контрольный список того, что все правильно интегрировано, существует так много состояний, возможных ситуаций (частичные продукты лежат вокруг, переполненные корзины, отсутствуют), которые трудно дать гарантии.
А если серьезно, процедурный код не определяет семантику желаемого результата так, как функциональный код. Процедурные программисты могут легче избавиться от косвенного кода и данных и предложить несколько способов сделать что-то одно (некоторые из них несовершенны). Обычно посторонние данные создаются. Функциональные программисты могут занять больше времени, когда проблема становится более сложной?
Сильно типизированный функциональный язык все еще может лучше анализировать данные и потоки. С процедурным языком цель программы часто должна быть определена вне программы, как формальный анализ правильности.
источник