Di Elm, saya tidak tahu kapan type
adalah vs kata kunci yang sesuai type alias
. Dokumentasi tampaknya tidak memiliki penjelasan tentang ini, saya juga tidak dapat menemukannya di catatan rilis. Apakah ini didokumentasikan di suatu tempat?
Jawaban:
Bagaimana saya memikirkannya:
type
digunakan untuk menentukan jenis serikat baru:
type Thing = Something | SomethingElse
Sebelum definisi ini Something
dan SomethingElse
tidak berarti apa-apa. Sekarang keduanya adalah tipe Thing
, yang baru saja kita definisikan.
type alias
digunakan untuk memberi nama pada beberapa tipe lain yang sudah ada:
type alias Location = { lat:Int, long:Int }
{ lat = 5, long = 10 }
memiliki tipe { lat:Int, long:Int }
, yang tadinya merupakan tipe yang valid. Tapi sekarang kita juga bisa mengatakan itu memiliki tipe Location
karena itu adalah alias untuk tipe yang sama.
Perlu dicatat bahwa berikut ini akan mengkompilasi dengan baik dan ditampilkan "thing"
. Meskipun kami menetapkan thing
adalah a String
dan aliasedStringIdentity
mengambil AliasedString
, kami tidak akan mendapatkan kesalahan bahwa ada jenis ketidakcocokan antara String
/ AliasedString
:
import Graphics.Element exposing (show)
type alias AliasedString = String
aliasedStringIdentity: AliasedString -> AliasedString
aliasedStringIdentity s = s
thing : String
thing = "thing"
main =
show <| aliasedStringIdentity thing
{ lat:Int, long:Int }
tidak mendefinisikan tipe baru. Itu sudah tipe yang valid. type alias Location = { lat:Int, long:Int }
juga tidak mendefinisikan tipe baru, itu hanya memberi nama lain (mungkin lebih deskriptif) untuk tipe yang sudah valid. type Location = Geo { lat:Int, long:Int }
akan mendefinisikan tipe baru ( Location
)
Kuncinya adalah kata alias
. Dalam kursus pemrograman, ketika Anda ingin mengelompokkan hal-hal yang menjadi satu, Anda memasukkannya ke dalam sebuah catatan, seperti dalam kasus sebuah poin
{ x = 5, y = 4 }
atau catatan siswa.
{ name = "Billy Bob", grade = 10, classof = 1998 }
Sekarang, jika Anda perlu menyebarkan record ini, Anda harus mengeja seluruh tipe, seperti:
add : { x:Int, y:Int } -> { x:Int, y:Int } -> { x:Int, y:Int }
add a b =
{ a.x + b.x, a.y + b.y }
Jika Anda bisa membuat alias sebuah titik, tanda tangannya akan jauh lebih mudah untuk ditulis!
type alias Point = { x:Int, y:Int }
add : Point -> Point -> Point
add a b =
{ a.x + b.x, a.y + b.y }
Jadi alias adalah singkatan dari sesuatu yang lain. Di sini, ini adalah singkatan dari tipe rekaman. Anda dapat menganggapnya sebagai memberi nama untuk jenis rekaman yang akan sering Anda gunakan. Itulah mengapa ini disebut alias - ini adalah nama lain untuk tipe rekaman telanjang yang diwakili oleh{ x:Int, y:Int }
Padahal type
memecahkan masalah yang berbeda. Jika Anda berasal dari OOP, itu adalah masalah yang Anda selesaikan dengan warisan, kelebihan beban operator, dll. - terkadang, Anda ingin memperlakukan data sebagai hal yang umum, dan terkadang Anda ingin memperlakukannya seperti hal tertentu.
Hal yang biasa terjadi adalah saat menyebarkan pesan - seperti sistem pos. Saat Anda mengirim surat, Anda ingin sistem pos memperlakukan semua pesan sebagai hal yang sama, jadi Anda hanya perlu merancang sistem pos sekali. Selain itu, tugas perutean pesan harus independen dari pesan yang terkandung di dalamnya. Hanya ketika surat itu mencapai tujuannya barulah Anda peduli tentang apa pesannya.
Dengan cara yang sama, kita dapat mendefinisikan a type
sebagai gabungan dari semua jenis pesan berbeda yang dapat terjadi. Katakanlah kita menerapkan sistem pesan antara mahasiswa kepada orang tua mereka. Jadi hanya ada dua pesan yang dapat dikirim oleh anak-anak perguruan tinggi: 'Saya butuh uang bir' dan 'Saya butuh celana dalam'.
type MessageHome = NeedBeerMoney | NeedUnderpants
Jadi sekarang, ketika kita mendesain sistem perutean, tipe untuk fungsi kita bisa begitu saja MessageHome
, alih-alih mengkhawatirkan semua jenis pesan yang berbeda. Sistem perutean tidak peduli. Itu hanya perlu tahu itu a MessageHome
. Hanya ketika pesan mencapai tujuannya, rumah orang tua, Anda perlu mencari tahu apa itu.
case message of
NeedBeerMoney ->
sayNo()
NeedUnderpants ->
sendUnderpants(3)
Jika Anda mengetahui arsitektur Elm, fungsi pembaruan adalah pernyataan kasus raksasa, karena itulah tujuan dari mana pesan dirutekan, dan karenanya diproses. Dan kami menggunakan tipe gabungan untuk memiliki satu tipe yang harus ditangani saat meneruskan pesan, tetapi kemudian dapat menggunakan pernyataan kasus untuk mengetahui pesan apa itu sebenarnya, sehingga kami dapat mengatasinya.
Izinkan saya melengkapi jawaban sebelumnya dengan berfokus pada kasus penggunaan dan memberikan sedikit konteks pada fungsi dan modul konstruktor.
type alias
Membuat alias dan fungsi konstruktor untuk rekaman.
Ini kasus penggunaan yang paling umum: Anda dapat menentukan nama alternatif dan fungsi konstruktor untuk jenis format rekaman tertentu.
type alias Person =
{ name : String
, age : Int
}
Mendefinisikan alias tipe secara otomatis menyiratkan fungsi konstruktor berikut (kode pseudo):
Person : String -> Int -> { name : String, age : Int }
Ini bisa berguna, misalnya ketika Anda ingin menulis dekoder Json.
personDecoder : Json.Decode.Decoder Person
personDecoder =
Json.Decode.map2 Person
(Json.Decode.field "name" Json.Decode.String)
(Json.Decode.field "age" Int)
Tentukan bidang wajib
Mereka terkadang menyebutnya "catatan yang dapat diperluas", yang dapat menyesatkan. Sintaks ini dapat digunakan untuk menentukan bahwa Anda mengharapkan beberapa record dengan field tertentu. Seperti:
type alias NamedThing x =
{ x
| name : String
}
showName : NamedThing x -> Html msg
showName thing =
Html.text thing.name
Kemudian Anda dapat menggunakan fungsi di atas seperti ini (misalnya dalam tampilan Anda):
let
joe = { name = "Joe", age = 34 }
in
showName joe
Pembicaraan Richard Feldman tentang ElmEurope 2017 dapat memberikan wawasan lebih jauh tentang kapan gaya ini layak digunakan.
Mengganti nama barang
Anda dapat melakukan ini, karena nama baru dapat memberikan arti tambahan di kemudian hari dalam kode Anda, seperti dalam contoh ini
type alias Id = String
type alias ElapsedTime = Time
type SessionStatus
= NotStarted
| Active Id ElapsedTime
| Finished Id
Mungkin contoh yang lebih baik dari jenis penggunaan inti ini adalahTime
.
Mengekspos ulang tipe dari modul lain
Jika Anda menulis sebuah paket (bukan aplikasi), Anda mungkin perlu mengimplementasikan tipe dalam satu modul, mungkin dalam modul internal (tidak terekspos), tetapi Anda ingin mengekspos tipe dari modul yang berbeda (publik). Atau, sebagai alternatif, Anda ingin mengekspos tipe Anda dari beberapa modul.
Task
di inti dan Http.Request di Http adalah contoh untuk yang pertama, sedangkan pasangan Json.Encode.Value dan Json.Decode.Value adalah contoh di kemudian hari.
Anda hanya dapat melakukan ini jika sebaliknya Anda ingin mempertahankan tipe buram: Anda tidak mengekspos fungsi konstruktor. Untuk detailnya lihat penggunaan di type
bawah ini.
Perlu diperhatikan bahwa dalam contoh di atas hanya # 1 yang menyediakan fungsi konstruktor. Jika Anda mengekspos alias tipe Anda di # 1 seperti module Data exposing (Person)
itu akan mengekspos nama tipe serta fungsi konstruktor.
type
Definisikan tipe serikat yang diberi tag
Ini adalah kasus penggunaan yang paling umum, contoh yang baik adalah Maybe
tipe dalam inti :
type Maybe a
= Just a
| Nothing
Saat Anda mendefinisikan sebuah tipe, Anda juga mendefinisikan fungsi konstruktornya. Dalam kasus Maybe ini adalah (pseudo-code):
Just : a -> Maybe a
Nothing : Maybe a
Artinya jika Anda mendeklarasikan nilai ini:
mayHaveANumber : Maybe Int
Anda dapat membuatnya dengan salah satunya
mayHaveANumber = Nothing
atau
mayHaveANumber = Just 5
The Just
dan Nothing
tag tidak hanya berfungsi sebagai fungsi konstruktor, mereka juga berfungsi sebagai destructors atau pola dalam case
berekspresi. Yang berarti bahwa menggunakan pola-pola ini Anda dapat melihat di dalam a Maybe
:
showValue : Maybe Int -> Html msg
showValue mayHaveANumber =
case mayHaveANumber of
Nothing ->
Html.text "N/A"
Just number ->
Html.text (toString number)
Anda dapat melakukan ini, karena modul Maybe didefinisikan seperti
module Maybe exposing
( Maybe(Just,Nothing)
Bisa juga dikatakan
module Maybe exposing
( Maybe(..)
Keduanya setara dalam kasus ini, tetapi menjadi eksplisit dianggap kebajikan di Elm, terutama saat Anda menulis paket.
Menyembunyikan detail implementasi
Seperti yang ditunjukkan di atas, ini adalah pilihan yang disengaja bahwa fungsi konstruktor Maybe
terlihat untuk modul lain.
Namun, ada kasus lain ketika penulis memutuskan untuk menyembunyikannya. Salah satu contohnya adalahDict
. Sebagai pengguna paket, Anda seharusnya tidak dapat melihat detail implementasi algoritma pohon Merah / Hitam di belakang Dict
dan mengacaukan node secara langsung. Menyembunyikan fungsi konstruktor memaksa konsumen modul / paket Anda untuk hanya membuat nilai jenis Anda (dan kemudian mengubah nilai tersebut) melalui fungsi yang Anda tunjukkan.
Inilah alasan mengapa terkadang hal-hal seperti ini muncul dalam kode
type Person =
Person { name : String, age : Int }
Tidak seperti type alias
definisi di bagian atas posting ini, sintaks ini membuat jenis "gabungan" baru dengan hanya satu fungsi konstruktor, tetapi fungsi konstruktor tersebut dapat disembunyikan dari modul / paket lain.
Jika tipenya diekspos seperti ini:
module Data exposing (Person)
Hanya kode dalam Data
modul yang dapat membuat nilai Person dan hanya kode tersebut yang dapat mencocokkan pola padanya.
Perbedaan utama, menurut saya, adalah apakah pemeriksa tipe akan berteriak pada Anda jika Anda menggunakan tipe "sinomis".
Buat file berikut, letakkan di suatu tempat dan jalankan elm-reactor
, lalu buka http://localhost:8000
untuk melihat perbedaannya:
-- Boilerplate code
module Main exposing (main)
import Html exposing (..)
main =
Html.beginnerProgram
{
model = identity,
view = view,
update = identity
}
-- Our type system
type alias IntRecordAlias = {x : Int}
type IntRecordType =
IntRecordType {x : Int}
inc : {x : Int} -> {x : Int}
inc r = {r | x = .x r + 1}
view model =
let
-- 1. This will work
r : IntRecordAlias
r = {x = 1}
-- 2. However, this won't work
-- r : IntRecordType
-- r = IntRecordType {x = 1}
in
Html.text <| toString <| inc r
Jika Anda menghapus komentar 2.
dan berkomentar, 1.
Anda akan melihat:
The argument to function `inc` is causing a mismatch.
34| inc r
^
Function `inc` is expecting the argument to be:
{ x : Int }
But it is:
IntRecordType
An alias
hanyalah nama pendek untuk beberapa tipe lainnya, serupa class
di OOP. Kedaluwarsa:
type alias Point =
{ x : Int
, y : Int
}
Sebuah type
(tanpa alias) akan membiarkan Anda menentukan jenis Anda sendiri, sehingga Anda dapat menentukan jenis seperti Int
, String
, ... untuk Anda aplikasi. Sebagai contoh, dalam kasus umum, ini bisa digunakan untuk mendeskripsikan status aplikasi:
type AppState =
Loading --loading state
|Loaded --load successful
|Error String --Loading error
Jadi Anda dapat dengan mudah menanganinya di view
elm:
-- VIEW
...
case appState of
Loading -> showSpinner
Loaded -> showSuccessData
Error error -> showError
...
Saya pikir Anda tahu perbedaan antara type
dan type alias
.
Tapi mengapa dan bagaimana menggunakan type
dan type alias
penting dengan elm
aplikasi, kalian dapat merujuk artikel dari Josh Clayton