Який тип вищого роду у Скалі?


275

Ви можете знайти таке в Інтернеті:

  1. Конструктор вищого типу == type?

    class AClass[T]{...} // For example, class List[T]

    Деякі кажуть, що це тип вищого роду, оскільки він абстрагує типи, які відповідали б визначенню.

    Вищі типи роду - це типи, які приймають інші типи та будують новий тип

    Вони хоча і відомі як конструктор типів . (Наприклад, у програмуванні в Scala ).

  2. Вищий тип конструктора type ==, який приймає конструктор типу як параметр типу?

    У статті Generics of a Higher Kind ви можете прочитати

    ... типи, які абстрагуються над типами, які абстрагуються над типами ('типи вищого роду') ... "

    що говорить про це

    class XClass[M[T]]{...} // or
    
    trait YTrait[N[_]]{...} // e.g. trait Functor[F[_]]

    - тип вищого роду.

Отже, маючи на увазі, важко розрізнити тип конструктора , тип вищого типу та конструктор типу, який приймає конструктори типу як параметр типу , тому питання вище.


Як приклад додано функціонера Ландея.
Луцьк

Відповіді:


284

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

Конструктор типу - це тип, який можна застосувати до аргументів типу, щоб "сконструювати" тип.

Конструктор значень - це значення, яке можна застосувати до аргументів значення, щоб "сконструювати" значення.

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

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

Інтерпретація вищезазначених характеристик абстракцій першого порядку:

Конструктор типу - це тип, який можна застосувати до аргументів власного типу, щоб "сконструювати" належний тип.

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

Щоб підкреслити, що тут немає жодної абстракції (я думаю, ви могли б назвати це "нульовим порядком", але я цього не використовував ніде), наприклад, значення 1або тип String, ми зазвичай кажемо, що це "належне" значення або тип.

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

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

"Вищий порядок" - це просто загальний термін, що означає багаторазове використання поліморфізму / абстракції. Це означає те ж саме для поліморфних типів і значень. Конкретніше, абстракція вищого порядку абстрагує щось, що абстрагує щось. Для типів термін "вищий рід" - це версія спеціального призначення більш загального типу "вищого порядку".

Таким чином, версія нашої характеристики вищого порядку стає:

Конструктор типу - це тип, який можна застосувати до аргументів типу (належних типів або конструкторів типів), щоб "сконструювати" належний тип (конструктор).

Конструктор значень - це значення, яке ви можете застосувати до аргументів значення (власне значення або конструктори значень), щоб "сконструювати" власне значення (конструктор).

Таким чином, "вищого порядку" просто означає, що, коли ви говорите "абстрагування над X", ви насправді це маєте на увазі! Те, Xщо абстрагується, не втрачає власних "прав на абстракцію": воно може абстрагувати все, що хоче. (До речі, я використовую тут дієслово "абстракт", щоб означати: залишити те, що не є суттєвим для визначення значення чи типу, щоб воно могло бути змінено / надано користувачем абстракції як аргумент .)

Ось декілька прикладів (натхненні запитаннями Луца електронною поштою) правильних значень та типів першого порядку та вищого порядку:

                   proper    first-order           higher-order

values             10        (x: Int) => x         (f: (Int => Int)) => f(10)
types (classes)    String    List                  Functor
types              String    ({type λ[x] = x})#λ   ({type λ[F[x]] = F[String]})#λ

Де використовувані класи були визначені як:

class String
class List[T]
class Functor[F[_]]

Щоб уникнути непрямості через визначення класів, вам потрібно якось виразити функції анонімного типу, які не виражаються безпосередньо в Scala, але ви можете використовувати структурні типи без занадто синтаксичних накладних витрат ( стиль - завдяки https://stackoverflow.com / користувачів / 160378 / ретронім afaik):

У деякій гіпотетичній майбутній версії Scala, яка підтримує функції анонімного типу, ви можете скоротити цей останній рядок із прикладів до:

types (informally) String    [x] => x              [F[x]] => F[String]) // I repeat, this is not valid Scala, and might never be

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

ps: Для подальшого ускладнення питань "поліморфність" неоднозначно по-іншому, оскільки поліморфний тип іноді означає універсально кількісний тип, наприклад Forall T, T => T, який є належним типом, оскільки він класифікує поліморфні значення (у Scala це значення може бути написано як структурний тип {def apply[T](x: T): T = x})



5
Стаття "Поліморфізм конструктора типу" Адріана зараз на сайті adriaanm.github.com/research/2010/10/06/…
Стівен Шоу

Я продовжую читати це як вищого роду і уявляю собі спорідненого духу
Janac Meena

110

(Ця відповідь є спробою прикрасити відповідь Адріан-маврів деякою графічною та історичною інформацією.)

Вищі сорти входять до складу Scala з 2,5 року.

  • До цього Scala, як і Java до цих пір, не дозволяла використовувати конструктор типів ("generics" на Java) для використання в якості параметра типу конструктору типу. напр

     trait Monad [M[_]]

    було неможливо.

    У Scala 2.5 систему типів було розширено можливістю класифікувати типи на більш високому рівні (відомий як поліморфізм типу конструктора ). Ці класифікації відомі як види.

    Поняття типу та роду **, отримане ** із "Generics of a Higher Kind" (Зображення, похідне від Generics of a Higher Kind )

    Наслідком цього є те, що конструктор такого типу (наприклад List) може використовуватися так само, як і інші типи в типовому положенні параметрів типу конструкторів типів, і тому вони стали типом першого класу після Scala 2.5. (Подібно до функцій, які є значеннями першого класу у Scala).

    У контексті типової системи, що підтримує більш високі типи, ми можемо виділити власні типи , типи типу Intабо List[Int]від типів першого порядку, такі як Listі типи вищого типу, як Functorабо Monad(типи, які абстрагуються над типами, які абстрактні над типами).

    Система типів Java з іншого боку не підтримує види і тому не має типів "вищого типу".

    Тож це слід бачити на тлі системи підтримуючого типу.

  • У випадку Scala ви часто бачите приклади типу конструктора типу

     trait Iterable[A, Container[_]]

    з заголовком "Вищі типи роду", наприклад, у Scala для загальних програмістів, розділ 4.3

    Це іноді missleading, тому що багато хто називає в Containerякості вищого kinded типу , а не Iterable, але що більш точно,

    використання тут в Containerякості параметра конструктора типу вищого типу (вищого порядку) Iterable.


80

Вид звичайних типів , як Intі Char, чиї екземпляри значення, є *. Такий тип конструкторів одинарного типу Maybeє * -> *; конструктори бінарного типу, як, наприклад Either, ( кривим ) видом * -> * -> *тощо. Ви можете переглядати типи як Maybeі Eitherяк функції рівня типу: вони приймають один або кілька типів і повертають тип.

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

  • Замовлення 0: 1 :: Int
  • Замовлення 1: chr :: Int -> Char
  • Замовлення 2: fix :: (a -> a) -> a ,map :: (a -> b) -> [a] -> [b]
  • Замовлення 3: ((A -> B) -> C) -> D
  • Замовлення 4: (((A -> B) -> C) -> D) -> E

Отже, довга історія коротка, вищого роду тип типу - це лише функція вищого порядку на рівні типу.

  • Замовлення 0: Int :: *
  • Замовлення 1: Maybe :: * -> *
  • Порядок 2: Functor :: (* -> *) -> Constraint—вищий рівень: перетворює конструктори одинарного типу в обмеження типу

Гаразд, я зрозумів, що тоді буде прикладом у Scala для (* ⇒ *) ⇒ * і (* ⇒ *) ⇒ (* ⇒ *)? Чи міг би функціонер Ландея вписатись у першу категорію, а точніше у другу?
Луцьк

1
@lutz: Це було б у першій категорії: Functorстворює правильний тип (ну, ознака, але та сама ідея) Functor[F[_]]від конструктора типів F.
Джон Перді

1
@Jon: Дуже проникливий пост, дякую. Чи можна перетворювач типу (* => *) => (* => *)виразити у Scala? Якщо ні, то будь-якою іншою мовою?
Євген Лабун

@JonPurdy Порівняння між currying * ⇒ * ⇒ *дуже допомагає. Дякую!
Ліфу Хуан

(* ⇒ *) ⇒ (* ⇒ *)також може бути написано (* ⇒ *) ⇒ * ⇒ *. Це можна виразити в масштабі Scala Foo[F[_], T]. Це такий тип, як (у Haskell) newtype Twice f a = Twice (f (f a))(наприклад, Twice Maybe IntMaybe (Maybe Int), Twice [] Char[[Char]]) або щось більш цікаве, як вільна монада data Free f a = Pure a | Free (f (Free f a)).
Джон Перді

37

Я б сказав: Вищий тип рефератів над конструктором типу. Наприклад, врахуйте

trait Functor [F[_]] {
   def map[A,B] (fn: A=>B)(fa: F[A]): F[B]
}

Ось Functor"тип вищого роду" (як використовується в папері "Generics of a Higher Kind" ). Це не конкретний конструктор типу "першого порядку" типу List(який абстрагує лише належні типи). Він містить реферати над усіма одинарними конструкторами типу "першого порядку" (як позначається F[_]).

Або кажучи іншим чином: у Java ми чітко List<T>набираємо конструктори (наприклад ), але у нас немає "вищих типів", тому що ми не можемо абстрагуватися над ними (наприклад, ми не можемо записати Functorвизначений вище інтерфейс - принаймні не безпосередньо ).

Термін "поліморфізм вищого порядку (конструктор типу)" використовується для опису систем, що підтримують "типи вищого роду".


Це я думав, але це, здається, суперечить відповіді Йона, де «конструктор конкретного типу» - це вже «тип вищого роду».
Луцьк

3
Так. Відповідно до відповіді Джона (наскільки я це розумію) List<T>у Java було б конструктором одинарного типу, оскільки він чітко має такий вид * -> *. Чого не вистачає у відповіді Джона, це те, що ви повинні мати можливість абстрагуватися над «цілою справою» (а не лише над другою, *як у Java), щоб назвати її вищим видом.
Ландей

@Landai: У статті Scala для загальних програмістів у розділі 4.3 пропонується ознака Iterable [A, Container [_]] бути вищого типу (хоча не зрозуміло, чи мається на увазі Iterator або Container), де з іншого боку vero690 в Розділ 2.3.1 використовує термін конструктор вищого типу для подібного типу (* -> *) -> * (оператор типу, параметризований конструктором вищого порядку), який схожий на ітератор або ваш функтор Functor.
Луцьк

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

2
Загалом тут важливо розрізняти визначення та посилання. Визначення def succ(x: Int) = x+1вводить "конструктор значення" (див. Мою іншу відповідь на те, що я маю на увазі під цим) succ(ніхто не буде називати це значення як succ (x: Int)). За аналогією, чи Functorє (справді вищий) тип, визначений у вашій відповіді. Знову ж , ви не повинні ставитися до нього як Functor[F[_]](що , Fщо? _Вони не входять в комплект , до жаль, синтаксичний цукор для екзістенціалах каламутить води тут робить ?! F[_]Коротка для F[T forSome {type T}])
Адріаан маврів

0

Scala REPL забезпечує :kindкоманду, яка

scala> :help kind

:kind [-v] <type>
Displays the kind of a given type.

Наприклад,

scala> trait Foo[A]
trait Foo

scala> trait Bar[F[_]]
trait Bar

scala> :kind -v Foo
Foo's kind is F[A]
* -> *
This is a type constructor: a 1st-order-kinded type.

scala> :kind -v Foo[Int]
Foo[Int]'s kind is A
*
This is a proper type.

scala> :kind -v Bar
Bar's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :kind -v Bar[Foo]
Bar[Foo]'s kind is A
*
This is a proper type.

:helpДає чіткі визначення , так що я думаю , що варто розміщення його тут у всій своїй повноті (Scala 2.13.2)

scala> :help kind

:kind [-v] <type>
Displays the kind of a given type.

    -v      Displays verbose info.

"Kind" is a word used to classify types and type constructors
according to their level of abstractness.

Concrete, fully specified types such as `Int` and `Option[Int]`
are called "proper types" and denoted as `A` using Scala
notation, or with the `*` symbol.

    scala> :kind Option[Int]
    Option[Int]'s kind is A

In the above, `Option` is an example of a first-order type
constructor, which is denoted as `F[A]` using Scala notation, or
* -> * using the star notation. `:kind` also includes variance
information in its output, so if we ask for the kind of `Option`,
we actually see `F[+A]`:

    scala> :k -v Option
    Option's kind is F[+A]
    * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

When you have more complicated types, `:kind` can be used to find
out what you need to pass in.

    scala> trait ~>[-F1[_], +F2[_]] {}
    scala> :kind ~>
    ~>'s kind is X[-F1[A1],+F2[A2]]

This shows that `~>` accepts something of `F[A]` kind, such as
`List` or `Vector`. It's an example of a type constructor that
abstracts over type constructors, also known as a higher-order
type constructor or a higher-kinded type.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.