Чи є випадок використання нижнього типу як типу функціонального параметра?


12

Якщо функція має тип повернення ⊥ ( нижній тип ), це означає, що вона ніколи не повертається. Наприклад, він може вийти або викинути обидві досить звичайні ситуації.

Імовірно, якщо функція мала параметр типу ⊥, її ніколи не можна (безпечно) викликати. Чи існують якісь причини для визначення такої функції?

Відповіді:


17

Одним з визначальних властивостей або порожнього типу є те , що існує функція A для кожного типу A . Насправді існує унікальна така функція. Тому цілком розумно цю функцію надавати як частину стандартної бібліотеки. Часто його називають чимось подібним absurd. (У системах з підтипом це може бути вирішено просто, маючи бути підтипом кожного типу. Тоді неявна конверсія є absurd. Іншим пов'язаним підходом є визначення як α.α яке може бути просто інстанційним для будь-якого типу.)

Ви напевно хочете мати таку функцію або еквівалент, тому що саме це дозволяє використовувати функції, які виробляють . Наприклад, припустимо, що я дав тип суму E+A . Я роблю аналіз справи на ньому, і у випадку E я збираюся кинути виняток, використовуючи throw:E . В A разі, я буду використовувати f:AB . В цілому, я хочу значення типу B , так що мені потрібно зробити що - то , щоб перетворити в B . Ось що absurdдозволило б мені зробити.

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

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


Отже, це означає, що щось подібне (x ? 3 : throw new Exception())замінюється для аналізу на щось подібне (x ? 3 : absurd(throw new Exception()))?
bdsl

Якщо у вас не було підтипу або не було визначено як α . α , тоді перший не буде вводити перевірку, а другий. З підтипом, так, щось подібне буде неявно вставлено. Звичайно, ви можете помістити всередину визначення, яке є фактично тим, що визначає як α . α зробив би. α.αabsurdabsurdthrowα.α
Дерек Елкінс покинув SE

6

На додаток до сказаного про функцію absurd: ⊥ -> aя маю конкретний приклад того, де ця функція є корисною.

Розглянемо тип даних Haskell, Free f aякий представляє загальну структуру дерева з f-образними вузлами та листками, що містять as:

data Free f a = Op (f (Free f a)) | Var a

Ці дерева можна скласти за допомогою наступної функції:

fold :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
fold gen alg (Var x) = gen x
fold gen alg (Op x) = alg (fmap (fold gen alg) x)

Коротко цю операцію розміщують algу вузлах і genна листках.

Тепер до суті: всі рекурсивні структури даних можуть бути представлені за допомогою типу даних з фіксованою точкою. У Haskell це є Fix fі його можна визначити як type Fix f = Free f ⊥(тобто Дерева з f-образними вузлами і відсутністю листя поза функтором f). Традиційно ця структура має і складку, яку називають cata:

cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg x = fold absurd alg x

Що дає досить охайне використання абсурду: оскільки дерево не може мати жодного листя (оскільки ⊥ не має інших жителів крім undefined), його не можна використовувати genдля цієї складки та absurdілюструє це!


2

Нижній тип - це підтип кожного іншого типу, який може бути надзвичайно корисним на практиці. Наприклад, тип NULLв теоретично безпечній для версії C повинен бути підтипом будь-якого іншого типу вказівника, інакше ви не могли, наприклад, повернутися NULLтуди, де char*очікувалося; аналогічно, тип undefinedтеоретично безпечного типу JavaScript повинен бути підтипом усіх інших типів мови.

exit()throw()IntIntexit()

TST


3
NULLце тип одиниці, чи не так, від ⊥ який порожній тип?
бдсл

Я не впевнений, що ≺ означає в теорії типів.
бдсл

1
@bdsl Вигнутий оператор тут "є підтипом"; Я не впевнений, чи це стандарт, це просто те, чим користувався мій професор.
Драконіс

1
@ gnasher729 Щоправда, але C також не є особливо безпечним для типу. Я кажу, що якщо ви не можете просто надати ціле число void*, вам знадобиться певний тип, який може бути використаний для будь-якого типу вказівника.
Драконіс


2

Я можу придумати одне використання, і це щось, що розглядалося як вдосконалення мови програмування Swift.

У Свіфта є maybeмонада, писець Optional<T>або T?. Є багато способів взаємодії з ним.

  • Ви можете використовувати умовне розгортання, як

    if let nonOptional = someOptional {
        print(nonOptional)
    }
    else {
        print("someOptional was nil")
    }
    
  • Ви можете використовувати map, flatMapщоб перетворити значення

  • Оператор розгортання змусити ( !типу (T?) -> T) насильно розгортати вміст, інакше викликаючи збій
  • Оператор зрівнювання нуля ( ??, типу (T?, T) -> T), щоб прийняти його значення або іншим чином використовувати значення за замовчуванням:

    let someI = Optional(100)
    print(someI ?? 123) => 100 // "left operand is non-nil, unwrap it.
    
    let noneI: Int? = nil
    print(noneI ?? 123) // => 123 // left operand is nil, take right operand, acts like a "default" value
    

На жаль, не було чіткого способу сказати "видалити або викинути помилку" або "розкрутити або зірвати з користувацьким повідомленням про помилку". Щось на зразок

let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")

не компілюється, тому що fatalErrorмає тип () -> Never( ()є Void, тип одиниці NeverSwift, нижній тип Swift). Виклик його виробляє Never, що не сумісне з Tочікуваним, як правильним операндом ??.

Намагаючись виправити це, було висунуто пропагал Swift Evolution SE-0217- оператор “Unwrap or Die” . В остаточному підсумку він був відхилений , але він викликав зацікавленість Neverстати підтипом усіх типів.

Якщо це Neverбуло зроблено для підтипу всіх типів, то попередній приклад буде компілюватися:

let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")

тому що сайт виклику ??має тип (T?, Never) -> T, який був би сумісний з (T?, T) -> Tпідписом ??.


0

У Swift є тип "Ніколи", який, як видається, схожий на нижній тип: Функція, оголошена для повернення Ніколи не може повернутися, функцію з параметром типу Ніколи не можна викликати.

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

Для детальної інформації вам слід ознайомитися з новими публікаціями у списку розсилки швидкої еволюції.


7
"Новіші повідомлення в списку розсилки швидкої еволюції" - не дуже чітка або стабільна посилання. Чи немає веб-архіву списку розсилки?
Дерек Елкінс покинув SE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.