Kesan Pertama Elixir Bagi JavaScript Developer
Apa yang benar-benar saya menghargai pada tempat kerja saya ialah saya bisa secara mudah menelusuri bahasa baru lewat mobilitas internal. Sejauh karir saya di Schibsted, saya sudah membuat beberapa hal dengan JavaScript, TipeScript, Go, Kotlin, dan belakangan ini Elixir. Di mana kita memakai Elixir? Di Helthjem, yang disebut salah satunya perusahaan di bawah naungan Schibsted. Ini ialah perusahaan distribusi. Kami mengirim paket di depan pintu Anda (bila Anda ada di Norwegia ). Ini ialah domain dengan beberapa kejadian yang tersebar, jalur usaha yang kompleks, dan keperluan akan pelacakan paket yang akurat. Piranti lunak pokok kami perlu mengatasinya. Orang dapat benar-benar geram saat paket mereka terlambat atau bahkan juga bila mereka tidaklah sampai. Apa saya betul? Pada dasarnya, itu harus kuat. Elixir (dan kakaknya — Erlang), dikenali dengan mesin virtualnya, yakni BEAM. BEAM (dengan rangka kerja OTP) memungkinkannya pengembang membuat mekanisme terbagi yang toleran pada kesalahan memakai proses ringan dan penjadwal preemptive. Elixir dikenal juga untuk memberikan dukungan Phoenix/LiveView, rangka kerja situs yang paling dicintai. Setumpukan ini kerap dipakai di lingkungan start-up atau pembuatan prototipe cepat. Tidak berarti tidak pas untuk beban kerja produksi atau pemakaian perusahaan!
Dalam kasus permasalahan yang kami coba tuntaskan, sumber CQRS/Event dengan Elixir sangat cocok.
Dalam beberapa kalimat, saya meremas beberapa ide yang dapat membuat Anda pusing. Saya perlu membuat Anda bersedih bila Anda semangat dan mengharap untuk pelajari selanjutnya mengenai mereka di artikel ini. Saya tidak mengulasnya ini hari. Saya merencanakan untuk mengawali rangkaian artikel mengenai Teknologi ini disaksikan dari perspektif JavaScript developer, hingga lebih banyak mendatang. Ini hari kita akan konsentrasi cuma pada bahasa tersebut. Saya ingin ketahui mengenai kesan-kesan pertama Anda.
Code
Tanpa basa-basi kembali, silahkan langsung ke kodenya. Sering saat Anda dikenalkan dengan pangkalan code baru, Anda perlu pahami apa (dan bagaimana) proses usaha yang dijelaskannya. Bakal ada waktu untuk internal; berikut yang "mereka" ucapkan . Dengan anggapan jika kita perlu pecahkan permasalahan: membalik kata dalam kalimat… memakai teknik dua panduan.
input: "you must unlearn what you have learned"
output: "learned have you what unlearn must you"
Fungsi JavaScript dapat ditulis seperti pada daftar di bawah ini. Sebuah Loop while dengan nilai sementara untuk memodifikasi array kata berdasarkan referensi menurut indeks.
function reverseWords(sentence) {
let words = sentence.split(' ');
let left = 0;
let right = words.length - 1;
while (left < right) {
let temp = words[left];
words[left] = words[right];
words[right] = temp;
++left;
--right;
}
return words.join(' ');
}
module.exports = reverseWords
Logika yang sama, tetapi ditulis dalam Elixir:
defmodule Reverse do
def reverse_words(sentence) do
words = String.split(sentence)
reverse_items(words, 0, length(words) - 1)
|> Enum.join(" ")
end
defp reverse_items(words, left, right, result \\ %{})
defp reverse_items(_words, left, right, result) when left >= right do
Map.values(result)
end
defp reverse_items(words, left, right, result) do
result = Map.put(result, left, Enum.at(words, right))
result = Map.put(result, right, Enum.at(words, left))
reverse_items(words, left + 1, right - 1, result)
end
end
Untuk menjalankan fungsi itu, periksa repositori dan turuti README.md
. Node dan Elixir dibutuhkan.
Bila Anda berpikiran kenapa orang itu terlalu memperumit pekerjaan simpel semacam itu, jawabnya ialah: ini hanya sebuah karya fitur bahasa. Anda bisa menuntaskan permasalahan itu (tanpa dua panduan) cukup dengan mempersiapkan satu kalimat. Dalam Javascript: kalimat.split(‘ ‘)
.reverse()
.join(‘ ‘)
, dan dalam Elixir: kalimat |> String.split() |> Enum.reverse() |> Enum.join(" ").
Saya cukup percaya Anda bisa pahami code apa dari daftar di atas. Bila tidak, berharap baca dengan cermat dan dalami karena ini penting untuk pemikiran kita mendatang. Anda harus memerhatikan tiga hal:
- Mengapa tidak ada while loop?
- Mengapa fungsi reverse_items memiliki tiga definisi?
- Tanda aneh apa itu |>?
Di mana loop sementara?
Fungsi JavaScript dan Elixir sudah diartikan dalam modul. Kami memiliki dalam kedua bahasa. Kami memakainya sebagai ruangan nama untuk peranan Anda untuk organisasi code.
Apa saya mengatakan jika Elixir ialah bahasa fungsional? JavaScript mempunyai beberapa fitur yang memungkinkannya Anda lakukan pemrograman fungsional. JavaScript memungkinkannya Anda lakukan beberapa hal berbeda… khususnya yang ditanyakan. Berikut kenapa saya memandang bahasa ini cukup susah, karena sarat dengan perangkap. Peranan dalam kedua bahasa diberlakukan sebagai masyarakat negara kelas satu. Anda bisa memutuskan fungsi ke faktor, kembalikan fungsi, meneruskannya sebagai argumen, dan lain-lain.
Maka kami mempunyai modul dan bisa lakukan pemrograman fungsional, tapi bagaimana dengan while loop yang hilang itu? Loop adalah konstruksi berulang-ulang yang tidak ada dengan bahasa fungsional. Sebagai gantinya, Anda memakai rekursi atau memetakkan & mengurangi.
Bila Anda sudah memprogram dalam JavaScript untuk beberapa waktu, saya percaya Anda lebih suka mengulang koleksi pertimbangan memakai map() dan reduce(). Bukannya untuk atau sementara loop. Intuisi memberitahu Anda jika pendekatan ini lebih bersih, lebih gampang disaksikan sekilas, dan memperlihatkan saluran data secara jelas.
Mulai berpikiran secara rekursif ialah suatu hal yang membutuhkan waktu. Agar semakin spesifik mengenai tipe rekursi apa yang saya pakai, saya memakai rekursi ekor. Parameter keempat dari item_terbalik(kata, kiri + 1, kanan — 1, hasil)
ialah hasil. Ini ialah langkah untuk melanjutkan status ke iterasi selanjutnya. Tetapi, bila saya harus jujur, umumnya Anda tidak menyaksikan rekursi di program Elixir. Langkah untuk mengulang beberapa hal dengan memakai pola map & reduce. Minimal, berikut yang saya kerjakan.
Silahkan konsentrasi pada garis dari blok while: kata-kata[kiri] = kata-kata[kanan];
. Apa yang saya kerjakan di sini? Saya memutasi kalimat array. Dalam ruang cakupan tertentu (blok sementara), saya melakukan modifikasi banyak hal yang terhitung dalam ruang cakup luar. Kami menyebutkan sikap semacam itu sebagai efek. Elixir tidak mengizinkan Anda lakukan itu. Anda tidak temukan peranan apa pun itu atau langkah untuk melakukan modifikasi array "pada tempat" di API bahasa. Bahasa fungsional tidak meluluskan perubahan data. Tidak ada ruangan untuk ketidaktahuan seperti peranan split()
vs splice()
dalam JavaScript. Saksikan saja seberapa banyak hasil yang memberikan Anda istilah "JavaScript splice vs slice" di Google. Saya selalu usaha menghindar pemakaian beberapa fungsi ini karena saya tidak paham mana yang hendak mengganti array saya dan yang mana tidak. Untuk "mengupdate" faktor di Elixir, Anda bisa membuat daftar baru dan mengikatnya kembali lagi ke nama simbolis yang serupa:
numbers = [11, 99, 13]
# let's add 10 at the beginning
numbers = [11] ++ numbers
# let's add 14 at the end
numbers = numbers ++ [14]
Saya baru saja menunjukkan bagaimana cara menambahkan item di awal daftar dan bagaimana di akhir. Tapi bagaimana dengan menambahkan 13 di tengah angka untuk menjaga kesinambungan?
numbers = List.replace_at(numbers, 2, 12)
Tentu saja, Anda bisa melakukannya. Tapi dengan satu catatan penting. Seperti yang Anda ketahui, dalam JavaScript, semuanya adalah objek. Array dalam JavaScript adalah objek. Tingkat abstraksi data yang cukup tinggi, bukan? Di Elixir, kami tidak memiliki array; kami memiliki Daftar yang kami miliki. Di bawahnya, daftar diterapkan sebagai daftar tertaut tunggal. Jadi melakukan List.replace_at(angka, 2, 12)
adalah operasi linier — O(n). Anda harus memilih struktur data yang lebih efisien jika Anda perlu mengedit sesuatu dengan mereferensikan beberapa kunci — misalnya, Peta, yang berperilaku seperti tabel hash.
numbers = %{0 => 10, 1 => 11, 2 => 99, 3 => 13, 4 => 14}
numbers = Map.put(numbers, 2, 12) # O(1)
Omong-omong… dapatkah Anda menebak kompleksitas waktu yang merupakan operasi angka[2] = 12
dalam JavaScript?
Tiga definisi reverse_item
Saya punya teka-teki berikutnya untuk Anda. Apa yang akan dicetak oleh kode berikut?
function foo(arg1, arg2) { console.log(1) }
foo();
function foo(arg1) { console.log(2) }
function foo() { console.log(3) }
foo(1, 2, 3, 4);
Jika Anda tidak tahu apa dan mengapa, mulailah membaca tentang mengangkat di JavaScript. Apa maksud saya di sini? Intinya adalah "Anda hanya dapat memiliki" satu fungsi dengan nama yang diberikan dalam JavaScript. Anda tidak dapat membebaninya.
Definisi fungsi di Elixir dimulai dengan def (atau defp jika itu adalah fungsi pribadi), nama, daftar parameter, dan badan yang dilampirkan dalam blok do…end. Di dunia Elixir, kami menggunakan konvensi berikut untuk merujuk ke suatu fungsi: <nama modul>.<nama_fungsi>/<arity>
misalnya: Reverse.reverse_words/1. Arity fungsi mengacu pada jumlah argumen yang dibutuhkan. Ini penting, karena dua fungsi dengan nama yang sama tetapi berbeda arities adalah dua fungsi yang berbeda.
Hold the second cowboy… tetapi Anda memiliki tiga definisi fungsi dengan nama dan aritas yang sama. Ya itu benar. Anda dapat membebani fungsi di Elixir dengan menentukan banyak klausa. Teknik ini digunakan untuk membuat percabangan bersyarat.
Mari kita analisis semua klausa, satu per satu.
defp reverse_items(words, left, right, result \\ %{})
Yang pertama sebenarnya bukan klausa. Ini adalah kepala fungsi. Definisi fungsi tanpa tubuh. Kami menggunakannya ketika sebuah fungsi memiliki beberapa klausa dan nilai default. Dalam kasus kami, kami melakukannya untuk parameter hasil. Nilai default hasil
adalah %{}
(peta kosong). Kami melakukannya seperti itu untuk menjaga kode kami tetap bersih.
Klausa pertama hanya akan dipanggil ketika nilai left lebih besar atau sama dengan right. Saya menggunakan sesuatu yang disebut penjaga.
defp reverse_items(_words, left, right, result) when left >= right do
Map.values(result)
end
Anda mungkin memperhatikan bahwa tidak ada kata kunci pengembalian di badan fungsi. Nilai kembalian suatu fungsi adalah nilai kembalian dari ekspresi terakhirnya.
Dan klausa kedua… Kami menyebutnya klausa default karena cocok dengan segala sesuatu yang belum “ditangkap” sebelumnya. Kami menempatkannya sebagai yang terakhir.
Mengontrol sebuah flow dengan function overloading adalah salah satu teknik yang membuat kode ditulis dalam deklaratif Elixir. Anda mungkin pernah mendengar sesuatu tentang itu. Biasanya bertentangan dengan kode imperatif. Saya suka memikirkan kode deklaratif yang lebih mudah dipahami oleh orang non-teknis. Ini adalah topik yang menarik yang sulit untuk mendapatkan ide hanya dengan membaca tentang hal itu. Tapi begitu Anda mengerti, Anda akan mengalami momen eureka itu.
|>
Ini adalah operator pipe. Dibutuhkan output dari fungsi sebelumnya dan meneruskannya sebagai argumen pertama ke yang berikutnya.
Untuk mereplikasi perilaku yang sama dalam JavaScript, Anda akan menggunakan gaya tangga. Jika Anda lebih peduli, Anda akan menetapkan hasil dari fungsi berurutan ke variabel sementara.
Tangga dalam JavaScript:
function sumDiskSpace(input) {
return sumDirsSize(
getDirsBelow(
recalcDirSize(buildTree(input.split("\n"))),
{total_size: 100_000}
)
);
}
Dan sama tetapi dengan operator saluran pipe di Elixir:
def sum_disk_space(input) do
input
|> String.split("\n")
|> build_tree()
|> recalc_dir_size()
|> get_dirs_below(total_size: 100_000)
|> sum_dirs_size()
end
Anda akan melihat bahwa pola ini cukup sering digunakan. Ini mendorong pengembang untuk menulis fungsi yang dapat disusun. Yang pada akhirnya menghasilkan kode yang lebih baik.
Ringkasan
Saya mencoba membuatnya singkat. Saya melompat dari satu topik ke topik lainnya. Saya bahkan tidak menyebutkan pencocokan pola, yang merupakan esensi dari Elixir. Tujuan saya adalah menabur benih rasa ingin tahu. Jika Anda tertarik untuk lebih detail, silakan lihat https://elixir-lang.org. Ini adalah titik awal terbaik untuk belajar. Dalam dokumentasi ekosistem Elixir adalah teman Anda. Dan inilah yang saya maksud! Banyak topik menarik untuk dijelajahi ada di depan Anda. Saya berharap di salah satu artikel mendatang, saya membandingkan mesin virtual. Saya jamin itu akan menjadi bacaan yang bagus.