Apa jenis jenis yang lebih tinggi di Scala?

275

Anda dapat menemukan yang berikut di web:

  1. Jenis jenis yang lebih tinggi == jenis konstruktor?

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

    Ada yang mengatakan ini adalah jenis jenis yang lebih tinggi, karena abstrak atas jenis yang akan sesuai dengan definisi.

    Jenis yang lebih tinggi adalah jenis yang mengambil jenis lain dan membangun tipe baru

    Ini juga dikenal sebagai konstruktor tipe . (Misalnya, dalam Pemrograman dalam Scala ).

  2. Jenis lebih tinggi == jenis konstruktor yang mengambil konstruktor tipe sebagai parameter tipe?

    Dalam makalah Generics of a Kind , Anda dapat membaca

    ... jenis yang abstrak di atas jenis yang abstrak di atas jenis ('jenis yang lebih tinggi') ... "

    yang menyarankan itu

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

    adalah jenis yang lebih tinggi.

Jadi dengan mengingat hal ini, sulit untuk membedakan antara konstruktor tipe , tipe lebih tinggi dan konstruktor tipe yang menggunakan konstruktor tipe sebagai parameter tipe , oleh karena itu pertanyaan di atas.

Lutz
sumber
Menambahkan Functor Landei sebagai contoh.
Lutz

Jawaban:

284

Biarkan saya menebus permulaan dari kebingungan ini dengan melempar dengan beberapa disambiguasi. Saya suka menggunakan analogi ke tingkat nilai untuk menjelaskan ini, karena orang cenderung lebih akrab dengannya.

Tipe konstruktor adalah tipe yang bisa Anda terapkan untuk mengetik argumen untuk "membangun" suatu tipe.

Nilai konstruktor adalah nilai yang dapat Anda terapkan ke argumen nilai untuk "membangun" nilai.

Nilai konstruktor biasanya disebut "fungsi" atau "metode". "Konstruktor" ini juga dikatakan "polimorfik" (karena mereka dapat digunakan untuk membangun "barang" dengan "bentuk" yang berbeda), atau "abstraksi" (karena mereka abstrak atas apa yang bervariasi antara instantiasi polimorfik yang berbeda).

Dalam konteks abstraksi / polimorfisme, orde pertama mengacu pada "penggunaan tunggal" abstraksi: Anda mengabstraksi suatu tipe sekali, tetapi tipe itu sendiri tidak dapat mengabstraksi apa pun. Java 5 generics adalah first-order.

Interpretasi tingkat pertama dari karakterisasi abstraksi di atas adalah:

Tipe konstruktor adalah tipe yang dapat Anda terapkan untuk argumen tipe yang tepat untuk "membangun" tipe yang tepat.

Nilai konstruktor adalah nilai yang dapat Anda terapkan ke argumen nilai yang tepat untuk "membangun" nilai yang tepat.

Untuk menekankan tidak ada abstraksi yang terlibat (saya kira Anda bisa menyebutnya "zero-order", tetapi saya belum melihat ini digunakan di mana pun), seperti nilai 1atau tipe String, kami biasanya mengatakan sesuatu adalah nilai atau tipe yang "tepat".

Nilai yang tepat adalah "segera dapat digunakan" dalam arti tidak menunggu argumen (tidak abstrak atas mereka). Anggap saja sebagai nilai yang dapat Anda cetak / periksa dengan mudah (membuat serialisasi suatu fungsi curang!).

Tipe yang tepat adalah tipe yang mengklasifikasikan nilai (termasuk konstruktor nilai), konstruktor tipe tidak mengklasifikasikan nilai apa pun (mereka pertama-tama harus diterapkan pada argumen tipe yang tepat untuk menghasilkan tipe yang tepat). Untuk instantiate suatu tipe, perlu (tetapi tidak cukup) bahwa itu adalah tipe yang tepat. (Mungkin kelas abstrak, atau kelas yang Anda tidak memiliki akses.)

"Orde Tinggi" hanyalah istilah umum yang berarti penggunaan berulang polimorfisme / abstraksi. Ini berarti hal yang sama untuk tipe dan nilai polimorfik. Secara konkret, abstraksi tingkat tinggi mengabstraksi sesuatu yang abstrak atas sesuatu. Untuk jenis, istilah "jenis yang lebih tinggi" adalah versi tujuan khusus dari "tingkat tinggi" yang lebih umum.

Dengan demikian, versi tingkat tinggi dari karakterisasi kita menjadi:

Tipe konstruktor adalah tipe yang dapat Anda terapkan untuk mengetik argumen (tipe yang tepat atau tipe konstruktor) untuk "membangun" tipe yang tepat (konstruktor).

Nilai konstruktor adalah nilai yang dapat Anda terapkan ke argumen nilai (nilai yang tepat atau konstruktor nilai) untuk "membangun" nilai yang tepat (konstruktor).

Jadi, "orde yang lebih tinggi" berarti bahwa ketika Anda mengatakan "mengabstraksi lebih dari X", Anda benar-benar bersungguh-sungguh! Yang Xdiabstraksi tidak kehilangan "hak abstraksi" sendiri: ia dapat mengabstraksikan semua yang diinginkan. (Omong-omong, saya menggunakan kata kerja "abstrak" di sini untuk berarti: meninggalkan sesuatu yang tidak penting untuk definisi nilai atau tipe, sehingga dapat bervariasi / disediakan oleh pengguna abstraksi sebagai argumen .)

Berikut adalah beberapa contoh (terinspirasi oleh pertanyaan Lutz melalui email) tentang nilai dan jenis yang tepat, tingkat pertama dan tingkat tinggi:

                   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]})#λ

Di mana kelas yang digunakan didefinisikan sebagai:

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

Untuk menghindari tipuan melalui mendefinisikan kelas, Anda perlu entah bagaimana mengekspresikan fungsi tipe anonim, yang tidak dapat diekspresikan secara langsung di Scala, tetapi Anda dapat menggunakan tipe struktural tanpa terlalu banyak biaya sintaksis ( gaya - style adalah karena https://stackoverflow.com / users / 160378 / retronym afaik):

Di beberapa versi Scala masa depan hipotetis yang mendukung fungsi tipe anonim, Anda bisa mempersingkat baris terakhir dari contoh menjadi:

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

(Pada catatan pribadi, saya menyesal pernah berbicara tentang "tipe yang lebih baik", mereka hanya tipe! Ketika Anda benar-benar perlu untuk ambigu, saya sarankan mengatakan hal-hal seperti "ketik parameter konstruktor", "tipe anggota konstruktor" , atau "ketik konstruktor alias", untuk menekankan bahwa Anda tidak berbicara tentang tipe yang tepat saja.)

ps: Untuk memperumit masalah lebih lanjut, "polimorfik" bersifat ambigu dengan cara yang berbeda, karena tipe polimorfik kadang-kadang berarti tipe yang dikuantifikasi secara universal, seperti Forall T, T => T, yang merupakan tipe yang tepat, karena mengklasifikasikan nilai polimorfik (dalam Scala, nilai ini dapat berupa ditulis sebagai tipe struktural {def apply[T](x: T): T = x})

Adriaan Moor
sumber
1
lihat juga: adriaanm.github.com/research/2010/10/06/…
Adriaan Moors
5
Artikel "Type Constructor Polymorphism" karya Adriaan sekarang di adriaanm.github.com/research/2010/10/06/…
Steven Shaw
Saya terus membacanya sebagai saudara yang lebih tinggi dan membayangkan roh yang sama
Janac Meena
110

(Jawaban ini adalah upaya untuk menghias jawaban Adriaan Moor dengan beberapa informasi grafis dan historis.)

Jenis yang lebih tinggi adalah bagian dari Scala sejak 2.5.

  • Sebelum itu Scala, seperti Java sampai sekarang, tidak memungkinkan untuk menggunakan konstruktor tipe ("generik" di Jawa) untuk digunakan sebagai parameter tipe ke konstruktor tipe. misalnya

     trait Monad [M[_]]

    tidak mungkin.

    Dalam Scala 2.5 sistem tipe telah diperluas dengan kemampuan untuk mengklasifikasikan tipe pada tingkat yang lebih tinggi (dikenal sebagai polimorfisme konstruktor tipe ). Klasifikasi ini dikenal sebagai jenis.

    Jenis dan jenis realtion, ** berasal ** dari "Generics of a Kind Kind" (Gambar berasal dari Generics of a Kind Kind )

    Konsekuensinya adalah, konstruktor tipe tersebut (misalnya List) dapat digunakan sama seperti tipe lain dalam posisi parameter tipe konstruktor tipe dan karenanya mereka menjadi tipe kelas pertama sejak Scala 2.5. (Mirip dengan fungsi yang merupakan nilai kelas pertama di Scala).

    Dalam konteks sistem tipe yang mendukung jenis yang lebih tinggi, kita dapat membedakan jenis yang tepat , jenis suka Intatau List[Int]dari jenis orde pertama suka Listdan jenis jenis yang lebih tinggi seperti Functoratau Monad(jenis yang abstrak atas jenis yang abstrak atas jenis).

    Sistem tipe Java di sisi lain tidak mendukung jenis dan karenanya tidak memiliki jenis "jenis yang lebih tinggi".

    Jadi ini harus dilihat dengan latar belakang sistem tipe pendukung.

  • Dalam kasus Scala, Anda sering melihat contoh konstruktor tipe seperti

     trait Iterable[A, Container[_]]

    dengan judul "Jenis yang lebih tinggi", misalnya dalam Scala untuk programer generik, bagian 4.3

    Ini kadang-kadang missleading, karena banyak merujuk Containersebagai jenis kinded lebih tinggi dan tidak Iterable, tapi apa yang lebih tepat adalah,

    penggunaan Containerparameter tipe konstruktor sebagai tipe yang lebih tinggi (tingkat tinggi) di sini Iterable.

Lutz
sumber
80

The jenis jenis biasa seperti Intdan Char, yang contoh adalah nilai-nilai, adalah *. Jenis konstruktor tipe unary suka Maybeadalah * -> *; konstruktor tipe biner seperti jenis Either( kari ) * -> * -> *, dan sebagainya. Anda dapat melihat tipe suka Maybedan Eitherfungsi tipe-tingkat: mereka mengambil satu atau lebih tipe, dan mengembalikan tipe.

Fungsi adalah orde yang lebih tinggi jika memiliki orde lebih besar dari 1, di mana urutannya (secara informal) kedalaman bersarang, di sebelah kiri, panah fungsi:

  • Pesan 0: 1 :: Int
  • Pesanan 1: chr :: Int -> Char
  • Agar 2: fix :: (a -> a) -> a,map :: (a -> b) -> [a] -> [b]
  • Pesanan 3: ((A -> B) -> C) -> D
  • Pesanan 4: (((A -> B) -> C) -> D) -> E

Singkatnya, tipe yang lebih tinggi hanyalah fungsi level-level yang lebih tinggi.

  • Pesan 0: Int :: *
  • Pesanan 1: Maybe :: * -> *
  • Urutan 2: Functor :: (* -> *) -> Constraint—jenis yang lebih tinggi: mengonversi konstruktor tipe unary ke batasan kelas ketik
Jon Purdy
sumber
Ok saya mengerti, lalu apa yang akan menjadi contoh dalam Scala untuk (* ⇒ *) ⇒ * dan (* ⇒ *) ⇒ (* ⇒ *)? Apakah Functor of Landei masuk ke dalam kategori pertama atau lebih tepatnya di kategori kedua?
Lutz
1
@ Lutz: Ini akan berada di kategori pertama: Functormenghasilkan tipe yang tepat (well, trait, tetapi ide yang sama) Functor[F[_]]dari konstruktor tipe F.
Jon Purdy
1
@ Jon: Posting yang sangat mendalam, terima kasih. Apakah konverter tipe (* => *) => (* => *)dapat diekspresikan dalam Scala? Jika tidak, dalam bahasa apa pun?
Eugen Labun
@ JonPurdy Perbandingan antara * ⇒ * ⇒ *currying sangat membantu. Terima kasih!
Lifu Huang
(* ⇒ *) ⇒ (* ⇒ *)bisa juga dieja (* ⇒ *) ⇒ * ⇒ *. Itu bisa diekspresikan dalam Scala like Foo[F[_], T]. Ini jenis seperti (di Haskell) newtype Twice f a = Twice (f (f a))(mis., Twice Maybe IntMaybe (Maybe Int), Twice [] Char[[Char]]) atau sesuatu yang lebih menarik seperti monad gratis data Free f a = Pure a | Free (f (Free f a)).
Jon Purdy
37

Saya akan mengatakan: Jenis abstrak yang lebih tinggi di atas konstruktor tipe. Misalnya pertimbangkan

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

Ini Functoradalah "jenis yang lebih tinggi" (seperti yang digunakan dalam kertas "Generik dari Jenis yang Lebih Tinggi" ). Ini bukan konstruktor tipe konkret ("orde pertama") List(yang abstrak hanya tipe yang tepat). Ini abstrak atas semua konstruktor tipe unary ("orde pertama") (seperti dilambangkan dengan F[_]).

Atau dengan kata lain: Di Jawa, kami memiliki tipe konstruktor yang jelas (mis. List<T>), Tetapi kami tidak memiliki "tipe yang lebih tinggi", karena kami tidak dapat mengabstraksi mereka (mis. Kami tidak dapat menulis Functorantarmuka yang didefinisikan di atas - setidaknya tidak secara langsung ).

Istilah "polimorfisme orde tinggi (tipe konstruktor)" digunakan untuk menggambarkan sistem yang mendukung "tipe yang lebih tinggi".

Landei
sumber
Ini adalah apa yang saya pikirkan, tetapi tampaknya bertentangan dengan jawaban Jon, di mana "konstruktor tipe konkret" sudah menjadi "tipe yang lebih baik".
Lutz
3
Iya. Menurut jawaban Jon (seperti yang saya mengerti) List<T>di Jawa akan menjadi konstruktor tipe unary karena jelas jenisnya * -> *. Apa yang hilang dalam jawaban Jon adalah bahwa Anda harus mampu mengabstraksi "semua hal" (dan bukan hanya yang kedua *seperti di Jawa) untuk menyebutnya jenis yang lebih baik.
Landei
@Landai: Makalah Scala untuk programmer generik pada bagian 4.3 menyarankan sifat Iterable [A, Container [_]] menjadi tipe yang lebih tinggi (meskipun tidak jelas apakah Iterator atau Container dimaksudkan) di sisi lain vero690 di bagian 2.3.1 menggunakan istilah konstruktor tipe lebih tinggi untuk sesuatu seperti (* -> *) -> * (operator tipe parameter dengan konstruktor tipe orde tinggi) yang terlihat mirip dengan Iterator atau sifat Functor Anda.
Lutz
1
Ini mungkin benar, tapi saya pikir kita mulai membelah rambut di sini. Poin penting mengenai jenis yang lebih tinggi adalah bahwa tidak hanya konstruktor tipe yang terlibat (urutan polimorfisme konstruktor satu jenis), tetapi kita juga dapat mengabstraksi tipe beton dari konstruktor tipe tersebut (polimorfisme konstruktor tipe orde tinggi). Kami memiliki kemampuan untuk mengabstraksi apa pun yang kami inginkan tanpa batasan (mengenai tipe dan konstruktor tipe), yang membuatnya kurang menarik untuk menyebutkan semua versi yang mungkin dari fitur tersebut. Dan itu menyakitkan otak saya.
Landei
2
Secara umum, penting untuk membedakan definisi dan referensi di sini. Definisi tersebut def succ(x: Int) = x+1memperkenalkan "konstruktor nilai" (lihat jawaban saya yang lain untuk apa yang saya maksudkan dengan ini) succ(tidak ada yang akan menyebut nilai ini sebagai succ (x: Int)). Dengan analogi, Functorapakah tipe (memang yang lebih tinggi) didefinisikan dalam jawaban Anda. Sekali lagi, Anda tidak harus menyebutnya sebagai Functor[F[_]](apa Fapa? _Mereka tidak dalam lingkup sayangnya, gula sintaksis untuk existentials muddies air di sini dengan membuat?! F[_]Kependekan F[T forSome {type T}])
Adriaan Moors
1

Scala REPL menyediakan :kindperintah yang

scala> :help kind

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

Sebagai contoh,

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.

Ini :helpmemberikan definisi yang jelas jadi saya pikir layak mempostingnya di sini secara keseluruhan (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.
Mario Galic
sumber