1

Тема: Коваріянт та контрваріянт

Почиткав трохи про ці речі, і не можу зрозуміти одного прикладу.
Спочатку позначки покажу:

  1. A ≼ B    означає, що A  є підтипом B.

  2. A → B    це така функція, котра приймає аргумент з типом А, та повертає результат з типом B.

  3. x : A    означає, що x має тип A.

Уявімо, що у нас є наступні типи

Пундель ≼ Пес ≼ Тварина

тобто Пундель є підтипом Песика, а Песик є підтипом Тварини, ніби все логічно, йдемо від більшого (Тварини), до меншого (Пунделя).

Далі, у нас є така от функція

f : (Пес → Пес) → void

тобто, функція приймає іншу функцію, аргументом котрої є Пес, і котра повертає Песика.

Далі йде питання, яку з наступних чотирьох функцій можна передати в функцію, що я навів вище

  1. Пундель → Пундель

  2. Пундель → Тварина

  3. Тварина → Тварина

  4. Тварина → Пундель

На думку автора статті - четвертий варіянт є правильним Тварина → Пундель.

Пояснює він ось цим:
(Тварина → Пундель) ≼ (Пес → Пес)

Тобто, функція, що приймає Тварину, і повертає Пунделя, є підтипом функції, що приймає і повертає Песика.

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

Але я оце думаю - окей, але якщо Пес ≼ Тварина, то Пес матиме всі функції і властивості Тварини, але також може мати власні, свої.

Якщо ми матимемо ще один підтип Кіт ≼ Тварина, то ми ж зможемо передати його в (Тварина → Пундель), і тоді, всередині (Пес → Пес) ми не матимемо необхідних властивостей, адже будемо очікувати Песика, але отримаємо Котика.

Подякували: leofun01, Yola2

2

Re: Коваріянт та контрваріянт

Якщо ми матимемо ще один підтип Кіт ≼ Тварина, то ми ж зможемо передати його в (Тварина → Пундель)

ніт

3

Re: Коваріянт та контрваріянт

Згадуємо Принцип підстановки Лісков.
Припустимо, що є властивості (наприклад, функції, що здатні прийняти такі параметри):
Дихає(Тварина)
Бреше(Пес)
Сальто(Пундель)
Тод, вочевидь, можна робити також
Бреше(Пундель)
Дихає(Пес)
Дихає(Пундель).
А от Бреше(Тварина), Сальто(Пес) і Сальто(Тварина) - заборонені операції.

Тепер до задачі. Є функціональний тип f, що приймає функцію Пес->Пес (назвемо цю функцію g) і повертає void. Що може зробити f із функцією g? Може викликати з якимось параметром і обробити результат. Але функція, що приймає будь-який суперклас Пса, може прийняти параметром Пса, зокрема і функція Тварина->Пундель. А от функція, що приймає параметром Пунделя, Пса вже не обробить. Наприклад, g може робити лише Дихає() зі своїм параметром, і це буде валідна функція для цієї ситуації. Якщо g робить Сальто(), то вже далеко не факт, що будь-який пес його виконає, тому g не має права проголошувати аргументом Пунделя (хоча й може його прийняти і обробити - як Пса). А результат g буде оброблятися вже в f, і там має вміти робити все, що вміє Пес, тобто Бреше(). Зокрема, він може бути і Пунделем, бо ж Бреше(Пундель), правильно?

Подякували: FakiNyan, leofun012

4

Re: Коваріянт та контрваріянт

Ще раз: ми розглядаємо не обмеження на об'єкти, а обмеження на функцію, що ці об'єкти приймає. Тварина->void приймає й Пунделя, й Пса; а от Пундель->void не приймає ані Пса, ані Тварину. Тому функція Тварина->void може використовуватися замість будь-якої функції, що приймає Пса чи Пунделя.

Подякували: Arete, FakiNyan, leofun013

5

Re: Коваріянт та контрваріянт

Щось мені вчора спати хотілося.
Отже, є тип Пес, який є підтипом типу Тварина. Тобто все, що виконується для Тварини, виконується для Пса.
Є функція Дихає(Тварина) і є функція Бреше(Пес). Функція Дихає приймає і Пса, і Тварину. Функція Бреше приймає лише Пса. Тобто "приймає Пса" виконується і для Бреше, і для Дихає, а от "приймає Тварину" - лише для Дихає. Тобто тип Дихає є підтипом типу Бреше. У аргументів функцій все йде у зворотній бік.

Подякували: FakiNyan, leofun012

6

Re: Коваріянт та контрваріянт

тобто, для функції g головним є, аби аргумент не був "вужчим", ніж той, що приймає функція f, тобто, це має бути Пес, або вище, тобто Тварина, бо тип Пес має все, що й має тип Тварина, і якщо ми передамо функцію Тварина -> Пес, то в цій функції будуть використовуватися властивості Тварини, котрі також присутні на типі Пес, а якщо б ми передали функцію Пундель -> Пес, то це означатиме, що в функції використовуватимуться властивості Пунделя, котрих тип Пес може й не мати.

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

7

Re: Коваріянт та контрваріянт

FakiNyan написав:

тобто, для функції g головним є, аби аргумент не був "вужчим", ніж той, що приймає функція f,

Функція f приймає функцію Пес->Пес. Функція g приймає Пса. Це різні домени, аргумент не може бути вужчим, бо вони незрівняні: звичайний тип і функціональний.
Тут треба дуже обережно писати про об'єкти (функції і змінні) і типи (функціональні і звичайні).

8 Востаннє редагувалося FakiNyan (28.05.2021 11:46:19)

Re: Коваріянт та контрваріянт

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

9 Востаннє редагувалося FakiNyan (28.05.2021 11:48:02)

Re: Коваріянт та контрваріянт

koala написав:
FakiNyan написав:

тобто, для функції g головним є, аби аргумент не був "вужчим", ніж той, що приймає функція f,

Функція f приймає функцію Пес->Пес. Функція g приймає Пса. Це різні домени, аргумент не може бути вужчим, бо вони незрівняні: звичайний тип і функціональний.
Тут треба дуже обережно писати про об'єкти (функції і змінні) і типи (функціональні і звичайні).

Я помилився з f та g. Я мав на увазі g, та те, що ми передаємо в f, в першому дописі там я навів 4 можливих функціональних типи

10

Re: Коваріянт та контрваріянт

FakiNyan написав:

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

і ще тим, що вони з тими аргументами роблять.
Типами аргументів і того, що повертають, визначаються функціональні типи.

Подякували: FakiNyan, leofun012

11

Re: Коваріянт та контрваріянт

koala написав:
FakiNyan написав:

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

і ще тим, що вони з тими аргументами роблять.
Типами аргументів і того, що повертають, визначаються функціональні типи.

Ну я мав на увазі саме функціональні типи

12

Re: Коваріянт та контрваріянт

Можливо така задача пишеться на мові логічного програмування PROLOG? Спитайте свого "викладача". Тут просто, якщо зрозумієте як працює ця мова, то зрозумієте як працює така задача.

13

Re: Коваріянт та контрваріянт

Haskell, Prolog, яка різниця? Все одно щось зарозуміле.

14 Востаннє редагувалося FakiNyan (28.05.2021 15:13:20)

Re: Коваріянт та контрваріянт

то в мене ось тут все правильно? (я виправив помилки)

тобто, для того функціонального типу, котрий ми передаємо в функ. тип g, головним є, аби аргумент не був "вужчим" (підтипом), ніж аргумент g...

15 Востаннє редагувалося leofun01 (29.05.2021 13:41:43)

Re: Коваріянт та контрваріянт

FakiNyan написав:
  1. A ≼ B    означає, що A  є підтипом B.

  2. A → B    це така функція, котра приймає аргумент з типом А, та повертає результат з типом B.

  3. x : A    означає, що x має тип A.

Пундель ≼ Пес ≼ Тварина

f : (Пес → Пес) → void

яку з наступних чотирьох функцій можна передати в функцію, що я навів вище

  1. Пундель → Пундель

  2. Пундель → Тварина

  3. Тварина → Тварина

  4. Тварина → Пундель

На думку автора статті - четвертий варіянт є правильним Тварина → Пундель.

Пояснює він ось цим:
(Тварина → Пундель) ≼ (Пес → Пес)

Автор помилився. Правильна відповідь: 2. Пундель → Тварина
, і навіть не тому що (Пундель → Тварина) ≼ (Пес → Пес)

FakiNyan написав:

Якщо ми матимемо ще один підтип Кіт ≼ Тварина, то ми ж зможемо передати його в (Тварина → Пундель), і тоді, всередині (Пес → Пес) ми не матимемо необхідних властивостей, адже будемо очікувати Песика, але отримаємо Котика.

Правильно мислите. Будь-який компілятор таке не пропустить.

16 Востаннє редагувалося koala (29.05.2021 07:14:10)

Re: Коваріянт та контрваріянт

Так, бачу, потрібні приклади. Отже (на Python, щоб не плутатися)

def f(g):             #g: Пес->Пес
    пес1 = Пес()
    пес2 = g(пес1)
    Бреше(пес2)

def ПундельТварина(пундель): #Пундель->Тварина
    Сальто(пундель)
    тварина = Тварина() #а можна навіть і Кіт, він тварина
    return тварина      #чи того ж пунделя повернути, він теж тварина

def ТваринаПундель(тварина): #Тварина->Пундель
    Дихає(тварина)
    пундель = Пундель()
    return пундель

f(ПундельТварина) #викличе ПундельТварина(пес) і Сальто(пес). Помилка
f(ТваринаПундель) #викличе ТваринаПундель(пес), Дихає(пес) і Бреше(пундель), все ок

leofun01, воно ж ніби і в C# так само (англ.)?

Подякували: FakiNyan, leofun012

17

Re: Коваріянт та контрваріянт

Ось, робочий приклад: https://replit.com/@pavloslav/Contravariance
Працює, як і передбачалося, лише Тварина → Пундель

Подякували: FakiNyan, leofun012

18

Re: Коваріянт та контрваріянт

Я помилився. Зараз це сприймається як щось просте. А вчора я нагородив зайвих припущень.

19

Re: Коваріянт та контрваріянт

Ну так я теж он скільки наплутав.

Подякували: leofun011

20

Re: Коваріянт та контрваріянт

тепер обійміться та поцілуйтесь

Подякували: leofun01, koala2