Методы расширения в функциональном программировании, такие как F #

В объектно-ориентированном программировании наследование и виртуальные методы являются распространенным сценарием для создания расширяемого кода. В более сложных настройках factory методы (или структуры зависимостей) помогают расширить базовый код.

Каковы общие подходы в функциональном программировании (например, F #) для создания расширяемого кода?

Ответ 1

Фантастический вопрос!

Вот ответ, полученный из этой статьи в блоге:

Функциональная парадигма (то есть использование функций более высокого порядка) обеспечивает только одну форму расширяемости: функции более высокого порядка. Это позволяет вам учитывать "внутренние" функции. Например, код, который часто появляется с тем же первым и последним кодовыми блоками:

let f x =
  first x
  stuff1 x
  last x

let g x =
  first x
  stuff2 x
  last x

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

let hof stuff x =
  first x
  stuff x
  last x

let f = hof stuff1 x

let g = hof stuff2 x

Применение этого агрессивно приводит к таким шаблонам проектирования, как комбинаторы парсеров, и является очень мощным и легким методом для расширения кода. Однако он не делает типы данных расширяемыми.

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

  • Common Lisp имеет общую Lisp систему объектов (CLOS) и макросистему.
  • Стандарт ML имеет параметрический полиморфизм и систему модулей более высокого порядка.
  • OCaml добавил полиморфные варианты, объекты, необязательные аргументы и макросистему Camlp4.
  • Haskell имеет параметрический полиморфизм и классы типов, а Template Haskell добавляет макросы.
  • Scala имеет ООП в стиле Java с некоторыми дополнительными функциями.

Прочитайте замечательную монографию Криса Окасаки. Чисто функциональные структуры данных для некоторых замечательных примеров с использованием модулей более высокого порядка в стандартных классах ML и типов в Haskell. Прочитайте повторное использование кода через полиморфные варианты Жака Гаррига для описания того, как эта функция языка может использоваться для атаки на проблему выражения. Однако эти решения довольно редки в дикой природе, и, в частности, вы можете пройти долгий путь без них (например, в F #).

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

F # - другой зверь, потому что его требования к дизайну были бесшовной совместимостью с остальной частью .NET(которая накладывает OOP в стиле .NET) и прагматизмом. Следовательно, F # сохраняет ядро ​​ML с параметрическим полиморфизмом и добавляет объектную систему .NET. Таким образом, вы можете извлечь выгоду из простой расширяемости, предлагаемой универсальными функциями более высокого порядка и обычным ООП, но не из любых более эзотерических функций, таких как модули более высокого порядка, классы классов и макросы.

Единственная форма расширяемости F #, которая была впервые использована, - активные шаблоны. Они позволяют разделить код, который разрушает с помощью сопоставления шаблонов из конкретного представления данных. Это важный способ отделить код от данных и, следовательно, сделать его более многоразовым.

Ответ 2

Основной метод расширения в функциональном программировании

  • Написание функций более высокого порядка (функции, которые принимают другие функции в качестве параметров)
  • (Функциональный) полиморфизм (с использованием функций и типов, параметризованных по типу - в терминологии С#, общих типах и методах).

Например, вместо использования абстрактного метода (который изменяет некоторое состояние объекта) вы, вероятно, передадите функцию в качестве аргумента. Функция будет принимать все необходимое состояние для выполнения вычисления (сделанного абстрактным методом в OO), и оно вернет новое состояние (или что бы то ни было, результат вычисления).

Общий (полиморфный) код, такой как list<T>, является еще одним примером техники расширения. У вас есть структура данных и функции, работающие с ним (например, List.map), и вы можете использовать его вместе с ранее неизвестными типами (тип элемента списка) и указать поведение, специфичное для этого типа (например, предикат фильтрации). Списки - довольно простой пример, но он работает и для не-коллекционных типов.

В более сложных настройках существуют большие различия между языками программирования

  • В Haskell люди, вероятно, используют классы типов (которые немного похожи на более мощные интерфейсы).
  • В OCaml люди используют функторы (подобно классическому функциональному полиморфизму, но вы можете параметризовать и несколькими функциями и типами).
  • В F #, я думаю, люди обычно смешивают стандартные методы .NET(такие как интерфейсы) с базовыми функциональными методами (функции более высокого порядка, передавая функции как аргумент конструктору объектов и т.д.).

Это сочетание FP и OO - довольно мощная комбинация, поэтому вам, вероятно, не нужны более сложные вещи, такие как рамки зависимостей.