В последнее время меня интересуют некоторые концепции функционального программирования. Я использовал ООП уже некоторое время. Я вижу, как я могу построить довольно сложное приложение в ООП. Каждый объект будет знать, как делать то, что делает объект. Или что-нибудь, что делает класс родителей. Так что я могу просто сказать, Person().speak()
чтобы заставить человека говорить.
Но как мне делать подобные вещи в функциональном программировании? Я вижу, как функции являются первоклассными предметами. Но эта функция делает только одну конкретную вещь. Буду ли я просто использовать say()
метод с плавающей точкой и вызывать его с эквивалентом Person()
аргумента, чтобы я знал, что это за вещь?
Таким образом, я могу видеть простые вещи, просто как мне сделать сопоставимые ООП и объекты в функциональном программировании, чтобы я мог модульно структурировать и организовать свою базу кода?
Для справки, мой основной опыт работы с ООП - это Python, PHP и немного C #. Языки, на которые я смотрю, имеют функциональные особенности - Scala и Haskell. Хотя я склоняюсь к Скале.
Базовый пример (Python):
Animal(object):
def say(self, what):
print(what)
Dog(Animal):
def say(self, what):
super().say('dog barks: {0}'.format(what))
Cat(Animal):
def say(self, what):
super().say('cat meows: {0}'.format(what))
dog = Dog()
cat = Cat()
dog.say('ruff')
cat.say('purr')
Ответы:
Здесь вы действительно спрашиваете, как сделать полиморфизм в функциональных языках, то есть как создать функции, которые ведут себя по-разному в зависимости от своих аргументов.
Обратите внимание, что первый аргумент функции обычно эквивалентен «объекту» в ООП, но в функциональных языках вы обычно требуется отделить функции от данных, поэтому «объект», скорее всего, будет чистым (неизменным) значением данных.
Функциональные языки в целом предоставляют различные варианты достижения полиморфизма:
В качестве примера приведем реализацию вашей проблемы в Clojure с использованием мультиметодов:
Обратите внимание, что это поведение не требует определения явных классов: обычные карты работают нормально. Функция отправки (в данном случае: type) может быть любой произвольной функцией аргументов.
источник
Это не прямой ответ, и при этом он не обязательно на 100% точен, поскольку я не эксперт по функциональному языку. Но в любом случае, я поделюсь с вами своим опытом ...
Около года назад я был в такой же лодке, как и вы. Я сделал C ++ и C #, и все мои проекты всегда были очень тяжелыми для ООП. Я слышал о языках FP, прочитал некоторую информацию онлайн, пролистал книгу F #, но все еще не мог понять, как язык FP может заменить ООП или быть полезным в целом, так как большинство примеров, которые я видел, были слишком просты.
Для меня «прорыв» наступил, когда я решил изучать питон. Я скачал python, затем перешел на домашнюю страницу проекта euler и просто начал делать одну проблему за другой. Python не обязательно является языком FP, и вы, безусловно, можете создавать в нем классы, но по сравнению с C ++ / Java / C # он имеет гораздо больше конструкций FP, поэтому, когда я начал с ним играть, я принял сознательное решение не определить класс, если я не должен был.
Что мне показалось интересным в Python, так это то, насколько легко и естественно было брать функции и «сшивать» их для создания более сложных функций, и в конце концов ваша проблема все еще была решена путем вызова одной функции.
Вы указали, что при кодировании вы должны следовать принципу единой ответственности, и это абсолютно правильно. Но то, что функция отвечает за одну задачу, не означает, что она может выполнять только абсолютный минимум. В FP у вас все еще есть уровни абстракции. Таким образом, ваши высокоуровневые функции могут все еще выполнять «одну» вещь, но они могут делегировать функции более низкого уровня, чтобы реализовать более тонкие детали того, как эта «одна» вещь достигается.
Однако ключ к FP заключается в том, что у вас нет побочных эффектов. Пока вы рассматриваете приложение как простое преобразование данных с определенным набором входов и наборов выходов, вы можете написать код FP, который бы выполнил то, что вам нужно. Очевидно, что не каждое приложение хорошо вписывается в эту форму, но как только вы начнете это делать, вы удивитесь, сколько приложений подойдет. И здесь я думаю, что Python, F # или Scala сияют, потому что они дают вам конструкции FP, но когда вам нужно помнить ваше состояние и «вводить побочные эффекты», вы всегда можете прибегнуть к истинным и проверенным методам ООП.
С тех пор я написал целую кучу кода Python в качестве утилит и других вспомогательных сценариев для внутренней работы, и некоторые из них были значительно расширены, но, помня основные принципы SOLID, большая часть этого кода все еще получалась очень удобной в обслуживании и гибкой. Так же, как в ООП, ваш интерфейс является классом, и вы перемещаете классы по мере рефакторинга и / или добавления функциональности, в FP вы делаете то же самое с функциями.
На прошлой неделе я начал программировать на Java, и с тех пор почти ежедневно мне напоминают, что в ООП я должен реализовывать интерфейсы, объявляя классы с помощью методов, которые переопределяют функции, в некоторых случаях я могу добиться того же в Python, используя простое лямбда-выражение, например, 20-30 строк кода, которое я написал для сканирования каталога, в Python было бы 1-2 строками и без классов.
ФП сами по себе являются языками более высокого уровня. В Python (извините, мой единственный опыт работы с FP) я мог собрать понимание списка в другом понимании списка с добавленными лямбдами и прочим материалом, и все это было бы всего лишь 3-4 строками кода. В C ++ я мог бы абсолютно точно выполнить то же самое, но поскольку C ++ является более низким уровнем, мне пришлось бы писать гораздо больше кода, чем 3-4 строки, и по мере увеличения количества строк мое обучение SRP начиналось, и я начинал думать о том, как разделить код на более мелкие части (то есть, больше функций). Но в интересах удобства сопровождения и скрытия деталей реализации я бы поместил все эти функции в один класс и сделал их приватными. И вот, у вас это есть ... Я только что создал класс, тогда как в python я написал бы «return (.... lambda x: .. ....)»
источник
В Хаскеле самый близкий у вас "класс". Этот класс, хотя и не такой, как класс в Java и C ++ , будет работать для того, что вы хотите в этом случае.
В вашем случае так будет выглядеть ваш код.
Тогда вы можете иметь отдельные типы данных, адаптирующие эти методы.
РЕДАКТИРОВАТЬ: - Прежде чем вы можете специализироваться, скажем, для собаки, вы должны сказать системе, что собака является животным.
РЕДАКТИРОВАТЬ: - Для Wilq.
Теперь, если вы хотите использовать say в функции say foo, вам придется сказать haskell, что foo может работать только с Animal.
теперь, если вы позвоните foo с собакой, она будет лаять, если вы позвоните с кошкой, она будет мяукать.
Теперь вы не можете иметь любое другое определение функции сказать. Если say вызывается с чем-то, что не является животным, это вызовет ошибку компиляции.
источник
Функциональные языки используют 2 конструкции для достижения полиморфизма:
Создание полиморфного кода с этим совершенно отличается от того, как ООП использует наследование и виртуальные методы. Хотя оба из них могут быть доступны на вашем любимом языке ООП (например, C #), большинство функциональных языков (например, Haskell) поддерживают его до одиннадцати. Редко функционировать, чтобы быть не универсальным, и большинство функций имеют функции в качестве параметров.
Трудно объяснить, как это, и потребуется много времени, чтобы изучить этот новый способ. Но для этого нужно полностью забыть ООП, потому что в функциональном мире это не так.
источник
это действительно зависит от того, чего вы хотите достичь.
если вам просто нужен способ организации поведения на основе выборочных критериев, вы можете использовать, например, словарь (хэш-таблицу) с объектами-функциями. в питоне это может быть что-то вроде:
заметьте, однако, что (а) нет «экземпляров» собаки или кошки и (б) вам придется отслеживать «тип» ваших объектов самостоятельно.
как, например:
pets = [['martin','dog','grrrh'], ['martha', 'cat', 'zzzz']]
. тогда вы могли бы сделать понимание списка, как[animals[pet[1]]['say'](pet[2]) for pet in pets]
источник
Языки OO могут использоваться вместо языков низкого уровня, иногда для непосредственного взаимодействия с машиной. C ++ Конечно, но даже для C # есть адаптеры и тому подобное. Хотя написание кода для управления механическими деталями и минимального контроля над памятью лучше поддерживать как можно ближе к низкому уровню. Но если этот вопрос относится к текущему объектно-ориентированному программному обеспечению, такому как Line Of Business, веб-приложениям, IOT, веб-службам и большинству массово используемых приложений, то ...
Ответьте, если применимо
Читатели могут попробовать работать с сервис-ориентированной архитектурой (SOA). То есть DDD, N-Layered, N-Tiered, Hexagonal, что угодно. Я не видел, чтобы крупное бизнес-приложение эффективно использовало «Традиционные» ОО (Active-Record или Rich-Models), как это было описано в 70-х и 80-х годах в последнее десятилетие. (См. Примечание 1)
Ошибка не в ОП, но есть несколько проблем с вопросом.
Приведенный вами пример просто демонстрирует полиморфизм, это не рабочий код. Иногда подобные примеры воспринимаются буквально.
В FP и SOA Данные отделены от бизнес-логики. То есть данные и логика не идут вместе. Логика входит в службы, а данные (доменные модели) не имеют полиморфного поведения (см. Примечание 2).
Услуги и функции могут быть полиморфными. В FP вы часто передаете функции в качестве параметров другим функциям вместо значений. Вы можете сделать то же самое в OO Languages с такими типами, как Callable или Func, но он не работает безудержно (см. Примечание 3). В FP и SOA ваши Модели не являются Полиморфными, только ваши Услуги / Функции. (См. Примечание 4)
В этом примере плохой случай жесткого кодирования. Я говорю не только о красной строке "собака лает". Я также говорю о самих CatModel и DogModel. Что происходит, когда вы хотите добавить овец? Вы должны пойти в свой код и создать новый код? Зачем? В рабочем коде я бы предпочел просто AnimalModel со своими свойствами. В худшем случае, AmphibianModel и FowlModel, если их свойства и обработка настолько различны.
Это то, что я ожидал бы увидеть в текущем "ОО" языке:
Как вы переходите от классов в ОО к функциональному программированию? Как уже говорили другие; Вы можете, но на самом деле это не так. Суть вышесказанного заключается в том, чтобы продемонстрировать, что вам даже не следует использовать классы (в традиционном понимании мира) при выполнении Java и C #. Когда вы начнете писать код в сервис-ориентированной архитектуре (DDD, Многоуровневая, Многоуровневая, Шестиугольная и т. Д.), Вы станете на один шаг ближе к Функциональным, поскольку вы отделяете свои Данные (Доменные модели) от Логических функций (Сервисов).
ОО Язык на шаг ближе к ФП
Вы можете даже пойти немного дальше и разделить свои SOA-сервисы на два типа.
Необязательный тип класса 1 : общие службы реализации интерфейса для точек входа. Это могут быть «нечистые» точки входа, которые могут вызывать «чистые» или «нечистые» другие функции. Это могут быть ваши точки входа из RESTful API.
Необязательный тип класса 2 : Pure Business Logic Services. Это статические классы, которые имеют «чистую» функциональность. В ПП «Чистый» означает отсутствие побочных эффектов. Он нигде явно не устанавливает состояние или постоянство. (См. Примечание 5)
Поэтому, когда вы думаете о классах на объектно-ориентированных языках, которые используются в сервис-ориентированной архитектуре, это не только приносит пользу вашему OO-коду, но и делает функциональное программирование очень простым для понимания.
Заметки
Примечание 1 : Оригинальный объектно-ориентированный дизайн «Rich» или «Active-Record» все еще существует. Существует много унаследованного кода, подобного тому, когда люди «делали это правильно» десять или более лет назад. В прошлый раз я видел, что такой код (сделано правильно) был из видеоигры Codebase на C ++, где они точно контролировали память и имели очень ограниченное пространство. Нельзя сказать, что FP и сервис-ориентированные архитектуры - звери и не должны учитывать аппаратное обеспечение. Но они ставят возможность постоянно меняться, поддерживаться, иметь переменные размеры данных и другие аспекты в качестве приоритета. В компьютерных играх и искусственном интеллекте вы очень точно управляете сигналами и данными.
Примечание 2 : Модели предметной области не имеют полиморфного поведения и не имеют внешних зависимостей. Они «изолированы». Это не значит, что они должны быть на 100% анемичными. У них может быть много логики, связанной с их конструкцией и изменчивым изменением свойств, если это применимо. См. DDD «Объекты ценности» и сущности Эрика Эванса и Марка Симанна.
Примечание 3 : Линк и Лямбда очень распространены. Но когда пользователь создает новую функцию, он редко использует Func или Callable в качестве параметров, тогда как в FP было бы странно видеть приложение без функций, следующих этому шаблону.
Примечание 4 : Не путать полиморфизм с наследованием. CatModel может наследовать AnimalBase, чтобы определить, какими свойствами обычно обладает Animal. Но, как я показываю, такие модели - это запах кода . Если вы видите этот шаблон, вы можете подумать о том, чтобы разбить его и превратить в данные.
Примечание 5 : Чистые функции могут (и делают) принимать функции в качестве параметров. Входящая функция может быть нечистой, но может быть чистой. Для целей тестирования это всегда будет чисто. Но при производстве, хотя он рассматривается как чистый, он может содержать побочные эффекты. Это не меняет того факта, что чистая функция чистая. Хотя функция параметра может быть нечистой. Не путать! : D
источник
Вы могли бы сделать что-то вроде этого .. PHP
источник
$whostosay
становится типом объекта, который определяет, что будет выполнено. Выше можно изменить, чтобы дополнительно принять другой параметр,$whattosay
чтобы тип, который его поддерживает (например'human'
), мог использовать его.