Memahami apa yang dilakukan kata kunci 'tipe' di Scala

144

Saya baru mengenal Scala dan saya tidak dapat menemukan banyak tentang typekata kunci tersebut. Saya mencoba memahami apa arti ungkapan berikut:

type FunctorType = (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

FunctorType itu semacam alias, tetapi apa artinya?

Core_Dumped
sumber

Jawaban:

148

Ya, jenis alias FunctorType hanya singkatan untuk

(LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

Ketik alias sering digunakan untuk menjaga sisa kode sederhana: Anda sekarang dapat menulis

def doSomeThing(f: FunctorType)

yang akan ditafsirkan oleh kompiler sebagai

def doSomeThing(f: (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate)

Ini membantu untuk menghindari mendefinisikan banyak jenis kustom yang hanya tupel atau fungsi yang didefinisikan pada jenis lain, misalnya.

Ada juga beberapa kasus penggunaan menarik lainnya untuk type, seperti dijelaskan misalnya dalam bab ini dari Pemrograman di Scala .

Roland Ewald
sumber
198

Sebenarnya typekata kunci dalam Scala dapat melakukan lebih dari sekedar aliasing tipe yang rumit menjadi nama yang lebih pendek. Ini memperkenalkan anggota tipe .

Seperti yang Anda ketahui, sebuah kelas dapat memiliki anggota bidang dan anggota metode. Nah, Scala juga memungkinkan kelas untuk memiliki anggota tipe.

Dalam kasus khusus Anda type, memang, memperkenalkan alias yang memungkinkan Anda untuk menulis kode yang lebih ringkas. Sistem tipe hanya mengganti alias dengan tipe aktual ketika pemeriksaan tipe dilakukan.

Tetapi Anda juga dapat memiliki sesuatu seperti ini

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Seperti anggota kelas lainnya, anggota tipe juga bisa abstrak (Anda hanya tidak menentukan apa nilai mereka sebenarnya) dan dapat ditimpa dalam implementasi.

Anggota tipe dapat dilihat sebagai dual generik karena banyak hal yang dapat Anda terapkan dengan generik dapat diterjemahkan menjadi anggota tipe abstrak.

Jadi ya, mereka dapat digunakan untuk aliasing, tetapi jangan membatasi mereka hanya untuk ini, karena mereka adalah fitur yang kuat dari sistem tipe Scala.

Silakan lihat jawaban luar biasa ini untuk perincian lebih lanjut:

Scala: Jenis abstrak vs generik

Marius Danila
sumber
44
Penting untuk diingat bahwa menggunakan tipe di dalam kelas menciptakan anggota tipe bukan alias. Jadi, jika Anda hanya perlu tipe alias, tentukan di objek pendamping.
Rüdiger Klaehn
9

Saya menyukai jawaban dari Roland Ewald karena ia menggambarkan dengan penggunaan yang sangat sederhana alias tipe, dan untuk lebih detail memperkenalkan tutorial yang sangat bagus. Namun, karena use case lain diperkenalkan di posting ini bernama type member , saya ingin menyebutkan use case paling praktis darinya, yang sangat saya sukai: (bagian ini diambil dari sini :)

Tipe Abstrak:

type T

T di atas mengatakan bahwa jenis ini yang akan digunakan, belum diketahui, dan tergantung pada subkelas beton, itu akan ditentukan. Cara terbaik untuk memahami konsep pemrograman adalah dengan memberikan contoh: Misalkan Anda memiliki skenario berikut:

Tanpa Abstraksi Jenis

Di sini Anda akan mendapatkan kesalahan kompilasi, karena metode makan di kelas Cow dan Tiger tidak menimpa metode makan di kelas Hewan, karena tipe parameternya berbeda. Ini Rumput di kelas Sapi, dan Daging di kelas Harimau vs Makanan di kelas Hewan yang super kelas dan semua subclass harus sesuai.

Sekarang kembali ke mengetik abstraksi, dengan diagram berikut dan hanya menambahkan abstraksi jenis, Anda dapat menentukan jenis input, dalam subkelas sesuai itu sendiri.

Dengan Tipe Abstrak

Sekarang lihat kode-kode berikut:

  val cow1: Cow = new Cow
  val cow2: Cow = new Cow

  cow1 eat new cow1.SuitableFood
  cow2 eat new cow1.SuitableFood

  val tiger: Tiger = new Tiger
  cow1 eat new tiger.SuitableFood // Compiler error

Kompiler senang dan kami meningkatkan desain kami. Kita bisa memberi makan sapi kita dengan sapi. Makanan yang cocok dan penyusun mencegah kita memberi makan sapi dengan makanan yang cocok untuk Harimau. Tetapi bagaimana jika kita ingin membuat perbedaan antara jenis cow1 CocokFood dan cow2 SuitabeFood. Dengan kata lain, akan sangat berguna dalam beberapa skenario jika jalur yang kita gunakan untuk mengetik (tentu saja melalui objek) pada dasarnya penting. Berkat fitur-fitur canggih dalam scala, dimungkinkan:

Tipe ketergantungan jalur: Objek scala dapat memiliki tipe sebagai anggota. Arti jenisnya, tergantung pada jalur yang Anda gunakan untuk mengaksesnya. Path ditentukan oleh referensi ke objek (alias turunan dari sebuah kelas). Untuk menerapkan skenario ini, Anda perlu mendefinisikan kelas Grass di dalam Sapi, yaitu, Sapi adalah kelas luar dan Rumput adalah kelas dalam. Strukturnya akan seperti ini:

  class Cow extends Animal {
    class Grass extends Food
    type SuitableFood = Grass
    override def eat(food: this.SuitableFood): Unit = {}
  }

  class Tiger extends Animal {
    class Meat extends Food
    type SuitableFood = Meat
    override def eat(food: this.SuitableFood): Unit = {}
  }

Sekarang jika Anda mencoba untuk mengkompilasi kode ini:

  1. val cow1: Cow = new Cow
  2. val cow2: Cow = new Cow

  3. cow1 eat new cow1.SuitableFood
  4. cow2 eat new cow1.SuitableFood // compilation error

Pada baris 4 Anda akan melihat kesalahan karena Rumput sekarang menjadi kelas bagian dalam Sapi, oleh karena itu, untuk membuat turunan Rumput, kita memerlukan objek sapi dan objek sapi ini menentukan jalurnya. Jadi 2 objek sapi memunculkan 2 jalur berbeda. Dalam skenario ini, cow2 hanya ingin makan makanan yang khusus dibuat untuk itu. Begitu:

cow2 eat new cow2.SuitableFood

Sekarang semua orang senang :-)

Mehran
sumber
5

Hanya contoh untuk melihat cara menggunakan "ketik" sebagai alias:

type Action = () => Unit

Definisi di atas mendefinisikan Aksi sebagai alias dari jenis prosedur (metode) yang mengambil daftar parameter kosong dan yang mengembalikan Unit.

sofiene zaghdoudi
sumber