Чому (або чому ні) екзистенційні типи вважаються поганою практикою функціонального програмування?


43

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

Хто-небудь придумав простий послідовний спосіб зняти залежність від них у коді, який все ще зберігає деякі переваги? Або принаймні будь-які способи ковзання в абстракції, які дозволяють їх видалити, не вимагаючи значного кодування, щоб впоратися зі зміною?

Докладніше про екзистенційні типи можна прочитати тут ("якщо ти наважишся ..").


8
@RobertHarvey Я знайшов повідомлення в блозі, який змусив мене вперше замислитися над цим питанням: Haskell Antipattern: Existential Typeclass . Також у статті, що згадується Джої Адамсом, описано деякі проблеми з екзистенціалами в Розділі 3.1. Якщо у вас є протилежні аргументи, будь ласка, поділіться ними.
Петро Пудлак

5
@ PetrPudlák: Майте на увазі, що антипатерн взагалі не існує екзистенційних типів, а особливого їх використання, коли щось простіше і простіше (і краще підтримується в Haskell) зробить ту саму роботу.
CA McCann

10
Якщо ви хочете дізнатися, чому автор певного допису в блозі висловив думку, тоді людина, яку ви, ймовірно, повинні запитати - це автор.
Ерік Ліпперт

6
@Ptolemy Це питання думки. Використовуючи Haskell роками, я навряд чи уявляю, як використовувати функціональну мову, яка не має системи сильного типу.
Петро Пудлак

4
@Ptolemy: Мантра Haskell - це "якщо вона компілює, то вона працює", мантра Erlangs - "нехай вона руйнується". Динамічний набір тексту хороший як клей, але я особисто не буду будувати речі, використовуючи лише клей.
День

Відповіді:


8

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

Ця закономірність часто формулюється як відповідь на питання про те, як скласти список різнорідно набраних елементів, які реалізують один і той же тип класу. Наприклад, ви можете мати список значень, що мають Showекземпляри:

{-# LANGUAGE ExistentialTypes #-}

class Shape s where
   area :: s -> Double

newtype Circle = Circle { radius :: Double }
instance Shape Circle where
   area (Circle r) = pi * r^2

newtype Square = Square { side :: Double }
    area (Square s) = s^2

data AnyShape = forall x. Shape x => AnyShape x
instance Shape AnyShape where
    area (AnyShape x) = area x

example :: [AnyShape]
example = [AnyShape (Circle 1.0), AnyShape (Square 1.0)]

Проблема з таким кодом:

  1. Єдина корисна операція, яку ви можете виконати на пристрої, - AnyShapeце отримати його площу.
  2. Ще потрібно використовувати AnyShapeконструктор, щоб привести один із типів фігур у AnyShapeтип.

Отже, як виявляється, цей фрагмент коду насправді не дає вам нічого, що цей коротший:

class Shape s where
   area :: s -> Double

newtype Circle = Circle { radius :: Double }
instance Shape Circle where
   area (Circle r) = pi * r^2

newtype Square = Square { side :: Double }
    area (Square s) = s^2

example :: [Double]
example = [area (Circle 1.0), area (Square 1.0)]

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


Але це не означає, що екзистенційні типи є проблемою! Наприклад, в Rust вони мають особливість, яку називають об'єктами ознаки, яку люди часто описують як екзистенціальний тип над ознакою (версії Руста про класи класів). Якщо екзистенційні типи класів є антипатерном у Хаскеллі, чи це означає, що Руст вибрав погане рішення? Немає! Мотивація в світі Хаскелла полягає в синтаксисі та зручності, а не в принципі.

Більш математичним способом цього є вказівка ​​на те, що AnyShapeтип зверху Doubleє ізоморфним - між ними існує "конверсія без втрат" (ну, крім економії для точності з плаваючою комою):

forward :: AnyShape -> Double
forward = area

backward :: Double -> AnyShape
backward x = AnyShape (Square (sqrt x))

Отже, строго кажучи, ви не отримуєте і не втрачаєте жодної сили, обираючи один проти іншого. Що означає, що вибір повинен базуватися на інших чинниках, таких як зручність використання та продуктивність.


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

Тож загальна відповідь полягає в тому, що загальної відповіді немає. Про використання екзистенціальних типів можна судити лише в контексті, і відповіді можуть бути різними залежно від того, які особливості та синтаксис надаються різними мовами.


Це не ізоморфізм.
Райан Райх

2

Я не надто знайомий з Haskell, тому спробую відповісти на загальну частину питання як неакадемічний розробник C #.

Після читання, виявляється, що:

  1. Шаблони Java схожі на екзистенційні типи:

    Різниця між екзистенційними типами Scala та підстановкою Java на прикладі

  2. Підстановочні знаки не реалізовані повністю на C #: загальна дисперсія підтримується, але дисперсія сайту виклику не є:

    C # Generics: Wildcards

  3. Можливо, ця функція вам не потрібна щодня, але коли ви це зробите, ви відчуєте це (наприклад, потрібно ввести додатковий тип для того, щоб все працювало):

    Замісні знаки із загальними обмеженнями на C #

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

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