Чи функціональне програмування є набором об'єктно-орієнтованих?


26

Чим більше функціонального програмування я роблю, тим більше я відчуваю, що він додає додатковий шар абстракції, схожий на те, як шар лука - все це включає попередні шари.

Я не знаю, чи це правда, тому, виходячи з принципів ООП, з якими я працював роками, хтось може пояснити, наскільки функціональний чи не чітко зображує будь-який із них: інкапсуляція, абстракція, успадкування, поліморфізм

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

Я знаю, що Haskell може відповідати вимогам "інтерфейсів", але знову не впевнено, чи це метод є фактом функціоналу? Я здогадуюсь, що, можливо, те, що функтори мають математичну основу, ви могли б сказати, що це певна побудована в очікуванні функціоналу?

Будь ласка, детально опишіть, як ви вважаєте, функціонал чи не відповідає 4 принципам ООП.

Редагувати: Я добре розумію відмінності між функціональною парадигмою та об'єктно-орієнтованою парадигмою і розумію, що в ці дні існує багато мов багатопарадигми, які можуть робити і те, і інше. Я дійсно просто шукаю визначення того, як прямо "fp" (думаю, пурист, як haskell) може зробити будь-яку з 4 перерахованих речей, або чому він не може зробити жодну з них. тобто "інкапсуляція може бути виконана із закриттям" (або якщо я помиляюся в цьому переконанні, будь ласка, вкажіть, чому).


7
Ці 4 принципи "не роблять" ООП. OOP просто "вирішує" їх за допомогою класів, пошуку класів та їх примірників. Але я також хотів би відповісти, якщо є способи досягти цих функціональних програм.
Ейфорія

2
@Euphoric Залежно від визначення, це робить OOP.
Конрад Рудольф

2
@KonradRudolph Я знаю, що багато людей заявляють про ці речі та переваги, які вони приносять як унікальні властивості OOP. Якщо припустити, що "поліморфізм" означає "політизм підтипу", я можу піти з тим, що два останніх є невід'ємною частиною OOP. Але мені ще належить зіткнутися з корисним визначенням інкапсуляції та абстрагування, яке виключає підходи, що не належать до ООП. Ви можете приховати деталі та обмежити доступ до них просто добре навіть у Haskell. А у Haskell також є спеціальний поліморфізм, тільки не підтип поліморфізм - питання в тому, чи має значення "підтип"?

1
@KonradRudolph Це не робить його більш прийнятним. Якщо що-небудь, це стимул активізувати, а тим, хто поширює це, є підставою переосмислити це.

1
Абстракція є невід'ємною для будь-якого програмування, принаймні будь-якого програмування, що перевищує вихідний машинний код. Інкапсуляція існувала задовго до OOP, і це невід'ємно для функціонального програмування. Функціональна мова не повинна включати явний синтаксис ні для успадкування, ні для поліморфізму. Я думаю, що додає до "ні".
sdenham

Відповіді:


44

Функціональне програмування не є шаром над OOP; це зовсім інша парадигма. Можна зробити OOP у функціональному стилі (F # був написаний саме з цією метою), а на іншому кінці спектру є такі речі, як Haskell, які явно відкидають принципи орієнтації на об'єкти.

Ви можете робити інкапсуляцію та абстрагування будь-якою мовою, достатньо розвиненою для підтримки модулів та функцій. OO забезпечує спеціальні механізми інкапсуляції, але це не щось властиве OO. Суть ОО - друга пара, яку ви згадали: успадкування та поліморфізм. Концепція формально відома як заміна Ліскова, і ви не можете її отримати без підтримки на мовному рівні для об'єктно-орієнтованого програмування. (Так, підробляти це можливо в деяких випадках, але ви втрачаєте багато переваг, які OO приносить на стіл.)

Функціональне програмування не фокусується на заміні Ліскова. Він фокусується на підвищенні рівня абстракції, а також на мінімізації використання стану, що змінюється, і підпрограми з "побічними ефектами", що є функціональним програмістом, який використовують для створення підпрограм, які насправді щось роблять (на відміну від просто обчислення чогось) звуку страшно. Але знову ж таки, це абсолютно окремі парадигми, які можна використовувати разом чи ні, залежно від мови та майстерності програміста.


1
Ну, успадкування (у тих виключно рідкісних випадках, коли це потрібно) досяжне над складом, і воно чистіше, ніж успадкування на рівні типу. Поліморфізм природний, особливо за наявності поліморфних типів. Але звичайно я згоден, що ПП не має нічого спільного з ООП та його принципами.
SK-логіка

Це завжди можна підробити - ви можете реалізовувати об'єкти будь-якою мовою, яку ви вибрали. Я згоден з усім іншим, хоча :)
Eliot Ball

5
Я не думаю, що термін "побічні ефекти" був придуманий (або в основному використовується) функціональними програмістами.
sepp2k

4
@ sepp2k: Він не сказав, що вони винайшли цей термін, просто щоб вони володіли ним, використовуючи приблизно той самий тон, який зазвичай використовували для позначення дітей, які відмовляються виходити з газону.
Aaronaught

16
@Aaronaught це мене не турбують діти на моїй галявині, це їх криваві побічні ефекти! Якби вони просто перестали мутувати по всій моїй галявині, я б зовсім не заперечував проти них.
Джиммі Хоффа

10

Я вважаю наступну інтуїцію корисною для порівняння OOP та FP.

Замість того, щоб розглядати FP як надмножину OOP, подумайте про OOP та FP як про два альтернативних способи розгляду подібної основної моделі обчислень, у якій у вас є:

  1. Деяка операція, яка виконується,
  2. деякі вхідні аргументи до операції,
  3. деякі фіксовані дані / параметри, які можуть впливати на визначення операції,
  4. деяке значення результату та
  5. можливо побічний ефект.

В OOP це захоплено

  1. Метод, який виконується,
  2. вхідні аргументи методу,
  3. об'єкт, на який викликається метод, що містить деякі локальні дані у вигляді змінних членів,
  4. повернене значення методу (можливо, недійсне),
  5. побічні ефекти методу.

У FP це захоплено

  1. Закриття, яке виконується,
  2. вхідні аргументи закриття,
  3. захоплені змінні закриття,
  4. повернене значення закриття,
  5. можливі побічні ефекти закриття (у чистих мовах, як Haskell, це відбувається дуже контрольованим способом).

За допомогою такої інтерпретації об’єкт може розглядатися як сукупність закриттів (його методів), які фіксують однакові не локальні змінні (змінні елементи об'єкта, загальні для всіх закриттів у колекції). Цей погляд також підтверджується тим, що в об'єктно-орієнтованих мовах закриття часто моделюються як об'єкти точно одним методом.

Я думаю, що різні точки зору походять від того, що об'єктно-орієнтований погляд зосереджений на об'єктах (даних), а функціональний погляд зосереджений на функціях / закриттях (операціях).


8

Це залежить від того, кого ви запитаєте для визначення ООП. Запитайте п’ятьох людей, і ви, ймовірно, отримаєте шість визначень. У Вікіпедії сказано :

Спроби знайти консенсусне визначення або теорію за об'єктами не виявилися дуже успішними

Тож, коли хтось дає дуже остаточну відповідь, прийміть це із зерном солі.

З цього приводу слід зробити хороший аргумент, що так, FP - це супернабір OOP як парадигма. Зокрема , визначення Алана Кей терміном об'єктно-орієнтованого програмування не суперечить цьому поняттю (але це робить Крістен Найгаард ). Все, що Кей насправді хвилювало, було те, що все є об'єктом, і що логіка реалізується шляхом передачі повідомлень між об'єктом.

Можливо, цікавіше для вашого питання, про класи та об’єкти можна думати з точки зору функцій та закриттів, повернутих функціями (які виступають одночасно як класи та конструктори). Це дуже близько до прототипу на основі програмування, і насправді JavaScript дозволяє робити саме це.

var cls = function (x) {
    this.y = x;
    this.fun = function () { alert(this.y); };
    return this;
};

var inst = new cls(42);
inst.fun();

(Звичайно, JavaScript дозволяє мутувати значення, що є незаконним у суто функціональному програмуванні, але це також не потрібно в чіткому визначенні OOP.)

Однак, важливіше питання: чи є це осмисленою класифікацією ООП? Чи корисно думати про це як про підмножину функціонального програмування? Я думаю, що в більшості випадків це не так.


1
Я вважаю, що це може мати сенс роздумувати, куди слід звернути лінію, коли я переключаю парадигми. Якщо сказано, що немає способу досягти субтипічного поліморфізму у fp, то я не буду перешкоджати коли-небудь намагатися використовувати fp у моделюванні чогось, що добре би відповідало йому. Якщо все-таки це можливо, я можу зайняти час, щоб досягти хорошого способу цього (хоча хороший спосіб може бути неможливим), коли сильно працювати в просторі fp, але бажаючи підтипового поліморфізму в декількох просторах ніші. Очевидно, якщо більшість системи відповідає їй, однак, краще було б використовувати OOP.
Джиммі Хоффа

6

FP, як OO, не є чітко визначеним терміном. Є школи з різними, іноді суперечливими визначеннями. Якщо ви берете те, що у них є спільного, ви переходите до:

  • функціональне програмування - це програмування з функціями першого класу

  • Програмування ОО - це програмування з включенням поліморфізму, поєднаного принаймні з обмеженою формою динамічно вирішеної перевантаження. (Побічна примітка: в OO-колах поліморфізм прийнято означати включенням поліморфізму, тоді як в школах FP це означає параметричний поліморфізм.)

Все інше або присутнє в іншому місці, або в деяких випадках відсутнє.

FP і OO - це два інструменти побудови абстракцій. У кожного вони є свої сильні та слабкі сторони (наприклад, вони мають інший бажаний напрямок розширення в задачі про вираження), але жодна не є суттєво потужнішою за іншу. Ви можете побудувати систему OO над ядром FP ​​(CLOS - одна з таких систем). Ви можете використовувати структуру ОО для отримання функцій першого класу (див. Спосіб визначення функцій лямбда в C ++ 11, наприклад).


Я думаю, ви маєте на увазі "функції першого класу", а не "функції першого порядку".
dan_waterworth

Помилка ... C ++ 11 лямбда - це навряд чи першокласні функції: у кожного лямбда є свій спеціальний тип (для всіх практичних цілей, анонімна структура), несумісний із типовим типом вказівника функції. І std::function, до якого можуть бути призначені як функціональні вказівники, так і лямбда, це, безумовно, загальне, а не об'єктно-орієнтоване. Це не дивно, адже обмежена марка поліморфізму об'єктної орієнтації (політип поліморфізму) суворо менш потужна, ніж параметричний поліморфізм (навіть Хіндлі-Мілнер, не кажучи вже про повну F-омегу системи).
піон

Я не маю багато досвіду роботи з пуристичними функціональними мовами, але якщо ви можете визначити класи-статичні методи в межах закриття і передати їх у різні контексти, я б сказав, що ви (незручно, можливо) хоча б на півдорозі там на функціональні варіанти. Є багато способів обійти суворі парами на більшості мов.
Erik Reppen

2

Ні; OOP може розглядатися як набір процедурних програмувань і принципово відрізняється від функціональної парадигми тим, що він має стан, представлений у полях екземплярів. У функціональній парадигмі змінні - це функції, які застосовуються на постійних даних для отримання бажаного результату.

Насправді ви можете вважати функціональне програмування підмножиною OOP; якщо ви зробите всі свої заняття незмінними, ви можете вважати, що у вас є якесь функціональне програмування.


3
Незмінні класи не створюють функцій вищого порядку, перелічують розуміння чи закриття. Fp не є підмножиною.
Джиммі Хоффа

1
@Jimmy Hoffa: Ви можете легко імітувати більш високу функцію oreder, створивши клас, який має єдиний метод, який приймає або більше об'єктів подібного типу, а також повертає об'єкт подібного типу (тип, який має метод і не має полів) . Компергенія списку - це не те, що пов’язане з мовою програмування, а не парадигмою (Smalltalk підтримує її і є OOP). Закриття є в C # і також буде вставлено в Java.
m3th0dman

так, C # має закриття, але це тому, що це мульти парадигма, до C # (за що я вічно вдячний) було додано закриття разом із іншими фрагментами fp, але їхня присутність у мові oop не змушує їх виконуватись. Хороша думка про функцію вищого порядку, але інкапсуляція методу в класі дозволяє подібну поведінку.
Джиммі Хоффа

2
Так, але якщо ви використовуєте закриття для зміни стану, ви все-таки програмуєте функціональну парадигму? Справа в тому, що функціональна парадигма - це відсутність стану, а не функцій високого порядку, рекурсії чи закриття.
m3th0dman

Цікава думка щодо визначення fp .. Мені доведеться більше подумати над цим, дякую, що поділилися своїми спостереженнями.
Джиммі Хоффа

2

Відповідь:

У Вікіпедії є чудова стаття про функціональне програмування з деякими прикладами, про які ви просите. @Konrad Rudolph вже надав посилання на статтю про ООП .

Я не думаю, що одна парадигма - це супер набір іншої. Вони різні точки зору на програмування, і деякі проблеми краще вирішити з одного погляду, а деякі з іншого.

Ваше питання ще більше ускладнюється всіма реалізаціями FP та OOP. Кожна мова має свої примхи, які відповідають будь-якій добрій відповіді на ваше запитання.

Все більш тангенціальне Rambling:

Мені подобається думка, що така мова, як Скала, намагається дати тобі найкраще з обох світів. Я хвилююся, що це дає вам і ускладнення обох світів.

Java є мовою ОО, але версія 7 додала функцію "пробні ресурси", яку можна використовувати для імітації свого роду закриття. Тут він імітує оновлення локальної змінної "a" посеред іншої функції, не роблячи її видимою для цієї функції. У цьому випадку перша половина іншої функції - це конструктор ClosureTry (), а друга половина - метод close ().

public class ClosureTry implements AutoCloseable {

    public static void main(String[] args) {
        int a = 1;
        try(ClosureTry ct = new ClosureTry()) {
            System.out.println("Middle Stuff...");
            a = 2;
        }
        System.out.println("a: " + a);
    }

    public ClosureTry() {
        System.out.println("Start Stuff Goes Here...");
    }

    /** Interface throws exception, but we don't have to. */
    public void close() {
        System.out.println("End Stuff Goes Here...");
    }
}

Вихід:

Start Stuff Goes Here...
Middle Stuff...
End Stuff Goes Here...
a: 2

Це може бути корисним за призначенням відкрити потік, записати в потік і надійно закрити його або просто зв'язати дві функції таким чином, що ви не забудете викликати другу після виконання певної роботи між ними . Звичайно, він настільки новий і незвичний, що інший програміст може видалити блок проб, не усвідомлюючи, що вони щось порушують, тому наразі це такий собі антидіапазон, але цікаво, що це можна зробити.

Ви можете виразити будь-яку петлю на більшості імперативних мов як рекурсію. Об'єкти та змінні можна зробити незмінними. Процедури можна записати для мінімізації побічних ефектів (хоча я заперечую, що справжня функція на комп’ютері неможлива - час, необхідний для виконання, і процесор / диск / системні ресурси, які він споживає, неминучі побічні ефекти). Деякі функціональні мови можна зробити для багатьох, якщо не всіх об'єктно-орієнтованих операцій. Вони не повинні бути взаємовиключними, хоча деякі мови мають обмеження (наприклад, не дозволяють оновлювати змінні), що запобігають певним шаблонам (наприклад, поля, що змінюються).

Для мене найбільш корисними частинами об'єктно-орієнтованого програмування є приховування даних (інкапсуляція), обробка достатньо подібних об'єктів як однакових (поліморфізм) та збирання ваших даних та методів, які працюють над цими даними разом (об'єкти / класи). Спадщина може бути флагманом ООП, але для мене це найменш важлива і найменш використана частина.

Найбільш корисними частинами функціонального програмування є незмінність (лексеми / значення замість змінних), функції (відсутність побічних ефектів) та закриття.

Я не думаю, що це об'єктно-орієнтоване, але я мушу сказати, що одна з найкорисніших речей в інформатиці - це можливість оголосити інтерфейс, а потім мати різні функції і дані реалізувати цей інтерфейс. Мені також подобається мати декілька змінних фрагментів даних, з якими я можу працювати, тому, мабуть, мені не зручно в виключно функціональних мовах, хоча я намагаюся обмежити зміни та побічні ефекти у всіх своїх програмах.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.