diff --git a/content/2-instalasi-golang.md b/content/2-instalasi-golang.md
index 36332a3c4..a6d02da7a 100644
--- a/content/2-instalasi-golang.md
+++ b/content/2-instalasi-golang.md
@@ -4,13 +4,13 @@ Hal pertama yang perlu dilakukan sebelum bisa menggunakan Go adalah meng-*instal
Di sini penulis mencoba meringkas petunjuk instalasi pada *link* di atas, agar lebih mudah untuk diikuti terutama untuk pembaca yang baru belajar.
-> Go yang digunakan adalah versi **1.20**, direkomendasikan menggunakan versi tersebut.
+> Go yang digunakan adalah versi **1.22**, direkomendasikan menggunakan versi tersebut.
URL untuk mengunduh *installer* Go: https://golang.org/dl/. Silakan langsung unduh dari *link* tersebut lalu lakukan proses instalasi, atau bisa mengikuti petunjuk pada chapter ini.
## A.2.1. Instalasi Go *Stable*
-#### • Instalasi Go di Windows
+#### ◉ Instalasi Go di Windows
1. Download terlebih dahulu *installer*-nya di [https://golang.org/dl/](https://golang.org/dl/). Pilih *installer* untuk sistem operasi Windows sesuai jenis bit yang digunakan.
@@ -26,14 +26,14 @@ URL untuk mengunduh *installer* Go: https://golang.org/dl/. Silakan langsung und
> Sering terjadi, command `go version` tidak bisa dijalankan meskipun instalasi sukses. Solusinya bisa dengan restart CMD (tutup CMD, kemudian buka lagi). Setelah itu coba jalankan ulang command di atas.
-#### • Instalasi Go di MacOS
+#### ◉ Instalasi Go di MacOS
Cara termudah instalasi Go di MacOS adalah menggunakan [Homebrew](http://brew.sh/).
1. *Install* terlebih dahulu Homebrew (jika belum ada), caranya jalankan perintah berikut di **terminal**.
```bash
- $ ruby -e "$(curl -fsSL http://git.io/pVOl)"
+ $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
2. *Install* Go menggunakan command `brew`.
@@ -57,7 +57,7 @@ Cara termudah instalasi Go di MacOS adalah menggunakan [Homebrew](http://brew.sh
5. Jika output adalah sama dengan versi Go yang ter-*install*, menandakan proses instalasi berhasil.
-#### • Instalasi Go di Linux
+#### ◉ Instalasi Go di Linux
1. Unduh arsip *installer* dari [https://golang.org/dl/](https://golang.org/dl/), pilih installer untuk Linux yang sesuai dengan jenis bit komputer anda. Proses download bisa dilakukan lewat CLI, menggunakan `wget` atau `curl`.
diff --git a/content/A-array.md b/content/A-array.md
index 10af138d1..2d4e75f32 100644
--- a/content/A-array.md
+++ b/content/A-array.md
@@ -1,6 +1,8 @@
# A.15. Array
-Array adalah kumpulan data bertipe sama, yang disimpan dalam sebuah variabel. Array memiliki kapasitas yang nilainya ditentukan pada saat pembuatan, menjadikan elemen/data yang disimpan di array tersebut jumlahnya tidak boleh melebihi yang sudah dialokasikan. Default nilai tiap elemen array pada awalnya tergantung dari tipe datanya. Jika `int` maka tiap element zero value-nya adalah `0`, jika `bool` maka `false`, dan seterusnya. Setiap elemen array memiliki indeks berupa angka yang merepresentasikan posisi urutan elemen tersebut. Indeks array dimulai dari 0.
+Array adalah kumpulan data bertipe sama, yang disimpan dalam sebuah variabel. Array memiliki kapasitas yang nilainya ditentukan pada saat pembuatan, menjadikan elemen/data yang disimpan di array tersebut jumlahnya tidak boleh melebihi yang sudah dialokasikan.
+
+Default nilai tiap elemen array pada awalnya tergantung dari tipe datanya. Jika `int` maka tiap element zero value-nya adalah `0`, jika `bool` maka `false`, dan seterusnya. Setiap elemen array memiliki indeks berupa angka yang merepresentasikan posisi urutan elemen tersebut. Indeks array dimulai dari 0.
Contoh penerapan array:
@@ -14,13 +16,13 @@ names[3] = "law"
fmt.Println(names[0], names[1], names[2], names[3])
```
-Variabel `names` dideklarasikan sebagai `array string` dengan alokasi elemen `4` slot. Cara mengisi slot elemen array bisa dilihat di kode di atas, yaitu dengan langsung mengakses elemen menggunakan indeks, lalu mengisinya.
+Variabel `names` dideklarasikan sebagai `array string` dengan alokasi kapasitas elemen adalah `4` slot. Cara mengisi slot elemen array bisa dilihat di kode di atas, yaitu dengan langsung mengakses elemen menggunakan indeks, lalu mengisinya.
![Menampilkan elemen array](images/A_array_0_array.png)
## A.15.1. Pengisian Elemen Array yang Melebihi Alokasi Awal
-Pengisian elemen array pada indeks yang tidak sesuai dengan alokasi menghasilkan error. Contoh sederhana, jika array memiliki 4 slot, maka pengisian nilai slot 5 seterusnya adalah tidak valid.
+Pengisian elemen array pada indeks yang tidak sesuai dengan jumlah alokasi menghasilkan error. Contoh: jika array memiliki 4 slot, maka pengisian nilai slot 5 seterusnya adalah tidak valid.
```go
var names [4]string
@@ -31,7 +33,7 @@ names[3] = "law"
names[4] = "ez" // baris kode ini menghasilkan error
```
-Solusi dari masalah di atas adalah dengan menggunakan keyword `append`, yang nantinya pada chapter selanjutnya ([A.16. Slice](/A-slice.html)) akan kita bahas.
+Solusi dari masalah di atas adalah dengan menggunakan keyword `append`, yang pembahasannya ada pada chapter selanjutnya, ([A.16. Slice](/A-slice.html)).
## A.15.2. Inisialisasi Nilai Awal Array
@@ -44,11 +46,11 @@ fmt.Println("Jumlah element \t\t", len(fruits))
fmt.Println("Isi semua element \t", fruits)
```
-Penggunaan fungsi `fmt.Println()` pada data array tanpa mengakses indeks tertentu, akan menghasilkan output dalam bentuk string dari semua array yang ada. Teknik ini biasa digunakan untuk *debugging* data array.
+Penggunaan fungsi `fmt.Println()` pada data array tanpa mengakses indeks tertentu, menghasilkan output dalam bentuk string dari semua array yang ada. Teknik ini umum digunakan untuk keperluan *debugging* data array.
![Menghitung jumlah elemen dan menampilkan isi array](images/A_array_1_array_initialization_and_len.png)
-Fungsi `len()` dipakai untuk menghitung jumlah elemen sebuah array.
+Fungsi `len()` berfungsi untuk menghitung jumlah elemen sebuah array.
## A.15.3. Inisialisasi Nilai Array Dengan Gaya Vertikal
@@ -69,11 +71,11 @@ fruits = [4]string{
}
```
-Khusus untuk deklarasi array dengan cara vertikal, tanda koma wajib dituliskan setelah elemen, termasuk elemen terakhir. Jika tidak, maka akan muncul error.
+Khusus untuk deklarasi array dengan cara vertikal, tanda koma wajib dituliskan setelah setiap elemen (termasuk elemen terakhir), agar tidak memunculkan syntax error.
## A.15.4. Inisialisasi Nilai Awal Array Tanpa Jumlah Elemen
-Deklarasi array yang nilainya diset di awal, boleh tidak dituliskan jumlah lebar array-nya, cukup ganti dengan tanda 3 titik (`...`). Jumlah elemen akan di kalkulasi secara otomatis menyesuaikan data elemen yang diisikan.
+Deklarasi array yang nilainya diset di awal, boleh tidak dituliskan jumlah lebar array-nya, cukup ganti dengan tanda 3 titik (`...`). Metode penulisan ini membuat kapasitas array otomatis dihitung dari jumlah elemen array yang ditulis.
```go
var numbers = [...]int{2, 3, 2, 4, 3}
@@ -82,17 +84,19 @@ fmt.Println("data array \t:", numbers)
fmt.Println("jumlah elemen \t:", len(numbers))
```
-Variabel `numbers` akan secara otomatis memiliki jumlah elemen `5`, karena pada saat deklarasi disiapkan 5 buah elemen.
+Variabel `numbers` secara otomatis kapasitas elemennya adalah `5`.
![Deklarasi array menggunakan tanda 3 titik](images/A_array_1_1_array_dots.png)
## A.15.5. Array Multidimensi
-Array multidimensi adalah array yang tiap elemennya juga berupa array (dan bisa seterusnya, tergantung ke dalaman dimensinya).
+Array multidimensi adalah array yang tiap elemennya juga berupa array.
+
+> Level kedalaman array multidimensi adalah tidak terbatas, bisa saja suatu array berisi elemen array yang setiap elemennya juga adalah nilai array, dst.
-Cara deklarasi array multidimensi secara umum sama dengan cara deklarasi array biasa, dengan cara menuliskan data array dimensi selanjutnya sebagai elemen array dimensi sebelumnya.
+Cara deklarasi array multidimensi secara umum sama dengan array biasa, bedanya adalah pada array biasa, setiap elemen berisi satu nilai, sedangkan pada array multidimensi setiap elemen berisi array.
-Khusus untuk array yang merupakan sub dimensi atau elemen, boleh tidak dituliskan jumlah datanya. Contohnya bisa dilihat pada deklarasi variabel `numbers2` di kode berikut.
+Khusus penulisan array yang merupakan subdimensi/elemen, boleh tidak dituliskan jumlah datanya. Contohnya bisa dilihat pada deklarasi variabel `numbers2` di kode berikut.
```go
var numbers1 = [2][3]int{[3]int{3, 2, 3}, [3]int{3, 4, 5}}
@@ -102,13 +106,13 @@ fmt.Println("numbers1", numbers1)
fmt.Println("numbers2", numbers2)
```
-Kedua array di atas memiliki elemen yang sama.
+Kedua array di atas memiliki jumlah dan isi elemen yang sama.
![Array multidimensi](images/A_array_2_array_multidimension.png)
## A.15.6. Perulangan Elemen Array Menggunakan Keyword `for`
-Keyword `for` dan array memiliki hubungan yang sangat erat. Dengan memanfaatkan perulangan menggunakan keyword ini, elemen-elemen dalam array bisa didapat.
+Keyword `for` dan array memiliki hubungan yang sangat erat. Dengan memanfaatkan perulangan/looping menggunakan keyword ini, elemen-elemen dalam array bisa didapat.
Ada beberapa cara yang bisa digunakan untuk me-looping data array, yg pertama adalah dengan memanfaatkan variabel iterasi perulangan untuk mengakses elemen berdasarkan indeks-nya. Contoh:
@@ -126,7 +130,7 @@ Perulangan di atas dijalankan sebanyak jumlah elemen array `fruits` (bisa diketa
## A.15.7. Perulangan Elemen Array Menggunakan Keyword `for` - `range`
-Ada cara yang lebih sederhana me-looping data array, dengan menggunakan keyword `for` - `range`. Contoh pengaplikasiannya bisa dilihat di kode berikut.
+Ada cara lain yang lebih sederhana untuk operasi perulangan array, yaitu menggunakan kombinasi keyword `for` - `range`. Contoh pengaplikasiannya bisa dilihat di kode berikut.
```go
var fruits = [4]string{"apple", "grape", "banana", "melon"}
@@ -138,13 +142,11 @@ for i, fruit := range fruits {
Array `fruits` diambil elemen-nya secara berurutan. Nilai tiap elemen ditampung variabel oleh `fruit` (tanpa huruf s), sedangkan indeks nya ditampung variabel `i`.
-Output program di atas, sama dengan output program sebelumnya, hanya cara yang digunakan berbeda.
+Output program di atas, sama persis dengan output program sebelumnya, hanya saja cara yang diterapkan berbeda.
## A.15.8. Penggunaan Variabel Underscore `_` Dalam `for` - `range`
-Kadang kala ketika *looping* menggunakan `for` - `range`, ada kemungkinan di mana data yang dibutuhkan adalah elemen-nya saja, indeks-nya tidak. Sedangkan kode di atas, `range` mengembalikan 2 data, yaitu indeks dan elemen.
-
-Seperti yang sudah diketahui, bahwa di Go tidak memperbolehkan adanya variabel yang menganggur atau tidak dipakai. Jika dipaksakan, error akan muncul, contohnya seperti kode berikut.
+Terkadang, dalam penerapan *looping* menggunakan `for` - `range`, ada kebutuhan di mana yang dibutuhkan dari perulangan adlah adalah elemen-nya saja, sedangkan indeks-nya tidak, contoh:
```go
var fruits = [4]string{"apple", "grape", "banana", "melon"}
@@ -154,11 +156,11 @@ for i, fruit := range fruits {
}
```
-Hasil dari kode program di atas:
+Hasil dari kode program di atas adalah error, karena Go tidak memperbolehkan adanya variabel yang menganggur atau tidak dipakai.
![Error karena ada variabel yang tidak digunakan](images/A_array_4_for_range_error.png)
-Di sinilah salah satu kegunaan variabel pengangguran, atau underscore (`_`). Tampung saja nilai yang tidak ingin digunakan ke underscore.
+Di sinilah salah satu kegunaan dari variabel pengangguran, atau underscore (`_`). Tampung saja nilai yang tidak ingin digunakan ke underscore.
```go
var fruits = [4]string{"apple", "grape", "banana", "melon"}
@@ -172,7 +174,7 @@ Pada kode di atas, yang sebelumnya adalah variabel `i` diganti dengan `_`, karen
![For range tanpa indeks](images/A_array_5_for_range_underscore.png)
-Jika yang dibutuhkan hanya indeks elemen-nya saja, bisa gunakan 1 buah variabel setelah keyword `for`.
+Bagaiamana jika sebaliknya? Misal, yang dibutuhkan hanya indeks-nya saja, nilainya tidak penting. Maka cukup tulis satu variabel saja setelah keyword `for`, yaitu variabel penampung nilai indeks.
```go
for i, _ := range fruits { }
@@ -182,7 +184,7 @@ for i := range fruits { }
## A.15.9. Alokasi Elemen Array Menggunakan Keyword `make`
-Deklarasi sekaligus alokasi data array juga bisa dilakukan lewat keyword `make`.
+Deklarasi sekaligus alokasi kapasitas array juga bisa dilakukan lewat keyword `make`.
```go
var fruits = make([]string, 2)
@@ -192,7 +194,7 @@ fruits[1] = "manggo"
fmt.Println(fruits) // [apple manggo]
```
-Parameter pertama keyword `make` diisi dengan tipe data elemen array yang diinginkan, parameter kedua adalah jumlah elemennya. Pada kode di atas, variabel `fruits` tercetak sebagai array string dengan alokasi 2 slot.
+Parameter pertama keyword `make` diisi dengan tipe data elemen array yang diinginkan, parameter kedua adalah jumlah elemennya. Pada kode di atas, variabel `fruits` tercetak sebagai array string dengan kapasitas alokasi 2 slot.
---
diff --git a/content/A-buffered-channel.md b/content/A-buffered-channel.md
index fdbdc95a8..b5e16bf82 100644
--- a/content/A-buffered-channel.md
+++ b/content/A-buffered-channel.md
@@ -1,12 +1,12 @@
# A.32. Buffered Channel
-Proses transfer data pada channel secara default dilakukan dengan cara **un-buffered**, atau tidak di-buffer di memori. Ketika terjadi proses kirim data via channel dari sebuah goroutine, maka harus ada goroutine lain yang menerima data dari channel yang sama, dengan proses serah-terima yang bersifat blocking. Maksudnya, baris kode setelah kode pengiriman dan penerimaan data tidak akan di proses sebelum proses serah-terima itu sendiri selesai.
+Proses transfer data pada channel secara default dilakukan dengan metode **un-buffered** atau tidak di-buffer di memori. Ketika terjadi proses kirim data via channel dari sebuah goroutine, maka harus ada goroutine lain yang menerima data dari channel yang sama, dengan proses serah-terima yang bersifat blocking. Maksudnya, baris kode setelah kode pengiriman dan juga penerimaan data tidak akan diproses sebelum proses serah-terima itu sendiri selesai.
Buffered channel sedikit berbeda. Pada channel jenis ini, ditentukan angka jumlah buffer-nya. Angka tersebut menjadi penentu jumlah data yang bisa dikirimkan bersamaan. Selama jumlah data yang dikirim tidak melebihi jumlah buffer, maka pengiriman akan berjalan **asynchronous** (tidak blocking).
Ketika jumlah data yang dikirim sudah melewati batas buffer, maka pengiriman data hanya bisa dilakukan ketika salah satu data yang sudah terkirim adalah sudah diambil dari channel di goroutine penerima, sehingga ada slot channel yang kosong.
-Proses pengiriman data pada buffered channel adalah *asynchronous* ketika jumlah data yang dikirim tidak melebihi batas buffer. Namun pada bagian channel penerimaan data selalu bersifat *synchronous*.
+Proses pengiriman data pada buffered channel adalah *asynchronous* ketika jumlah data yang dikirim tidak melebihi batas buffer. Namun pada bagian channel penerimaan data selalu bersifat *synchronous* atau blocking.
![Analogi buffered channel](images/A_buffered_channel_1_anatomy.png)
@@ -49,21 +49,21 @@ func main() {
Pada kode di atas, parameter kedua fungsi `make()` adalah representasi jumlah buffer. Perlu diperhatikan bahwa nilai buffered channel dimulai dari `0`. Ketika nilainya adalah **3** berarti jumlah buffer maksimal ada **4**.
-Bisa dilihat terdapat IIFE goroutine yang isinya proses penerimaan data dari channel `messages`, untuk kemudian datanya ditampilkan. Setelah goroutine tersebut dieksekusi, perulangan dijalankan dengan di-masing-masing perulangan dilakukan pengiriman data. Total ada 5 data dikirim lewat channel `messages` secara sekuensial.
+Terdapat juga IIFE goroutine yang isinya proses penerimaan data dari channel `messages`, untuk kemudian datanya ditampilkan. Setelah goroutine tersebut dieksekusi, perulangan dijalankan dengan di-masing-masing perulangan dilakukan pengiriman data. Total ada 5 data dikirim lewat channel `messages` secara sekuensial.
![Implementasi buffered channel](images/A_buffered_channel_2_buffered_channel.png)
-Bisa dilihat output di atas, pada proses pengiriman data ke-4, diikuti dengan proses penerimaan data; yang kedua proses tersebut berlangsung secara blocking.
+Terlihat di output, proses pengiriman data indeks ke-4 adalah diikuti dengan proses penerimaan data yang proses transfernya sendiri dilakukan *syncrhonous* atau blocking.
Pengiriman data indeks ke 0, 1, 2 dan 3 akan berjalan secara asynchronous, hal ini karena channel ditentukan nilai buffer-nya sebanyak 3 (ingat, jika nilai buffer adalah 3, maka 4 data yang akan di-buffer). Pengiriman selanjutnya (indeks 5) hanya akan terjadi jika ada salah satu data dari ke-empat data yang sebelumnya telah dikirimkan sudah diterima (dengan serah terima data yang bersifat blocking). Setelahnya, pengiriman data kembali dilakukan secara asynchronous (karena sudah ada slot buffer ada yang kosong).
-Karena pengiriman dan penerimaan data via buffered channel terjadi tidak selalu sycnrhonous (tergantung jumlah buffer-nya), maka ada kemungkinan dimana eksekusi program selesai namun tidak semua data diterima via channel `messages`. Karena alasan ini pada bagian akhir ditambahkan statement `time.Sleep(1 * time.Second)` agar ada jeda 1 detik sebelum program selesai.
+Karena pengiriman dan penerimaan data via buffered channel terjadi tidak selalu synchronous (tergantung jumlah buffer-nya), maka ada kemungkinan dimana eksekusi program selesai namun tidak semua data diterima via channel `messages`. Karena alasan ini pada bagian akhir ditambahkan statement `time.Sleep(1 * time.Second)` agar ada jeda 1 detik sebelum program selesai.
-#### • Fungsi `time.Sleep()`
+#### ◉ Fungsi `time.Sleep()`
Fungsi ini digunakan untuk menambahkan delay sebelum statement berikutnya dieksekusi. Durasi delay ditentukan oleh parameter, misal `1 * time.Second` maka durasi delay adalah 1 detik.
-Lebih detailnya mengenai fungsi `time.Sleep()` dan `time.Second` dibahas pada chapter terpisah, yaitu [Time Duration](https://dasarpemrogramangolang.novalagung.com/A-time-duration.html).
+Lebih detailnya mengenai fungsi `time.Sleep()` dan `time.Second` dibahas pada chapter terpisah, yaitu [Time Duration](/A-time-duration.html).
---
diff --git a/content/A-channel-range-close.md b/content/A-channel-range-close.md
index 6f6b3357e..9ce0ee6a7 100644
--- a/content/A-channel-range-close.md
+++ b/content/A-channel-range-close.md
@@ -1,16 +1,17 @@
# A.34. Channel - Range dan Close
-Proses *retrieving* data dari banyak channel bisa lebih mudah dilakukan dengan memanfaatkan kombinasi keyword `for` - `range`.
+Proses penerimaan/*retrieving* data dari banyak channel bisa lebih mudah dilakukan dengan memanfaatkan kombinasi keyword `for` - `range`. Penerapannnya cukup mudah, yaitu dengan menuliskan keyword `for` - `range` pada variabel channel.
-`for` - `range` jika diterapkan pada channel berfungsi untuk handle penerimaan data. Setiap kali ada pengiriman data via channel, maka akan men-trigger perulangan `for` - `range`. Perulangan akan berlangsung terus-menerus seiring pengiriman data ke channel yang dipergunakan. Dan perulangan hanya akan berhenti jika channel yang digunakan tersebut di **close** atau di non-aktifkan. Fungsi `close` digunakan utuk me-non-aktifkan channel.
+Cara kerjanya:
-Channel yang sudah di-close tidak bisa digunakan lagi baik untuk menerima data ataupun untuk mengirim data, itulah mengapa perulangan `for` - `range` juga berhenti.
+- Transaksi data via channel men-trigger perulangan `for` - `range`. Perulangan akan berlangsung seiring terjadinya pengiriman data ke channel yang di-iterasi.
+- Perulangan tersebut hanya akan berhenti jika channel di-**close** atau di non-aktifkan via fungsi `close()`. Channel yang sudah di-close tidak bisa digunakan lagi baik untuk menerima data ataupun untuk mengirim data.
-## A.34.1. Penerapan `for` - `range` - `close` Pada Channel
+## A.34.1. Penerapan `for` - `range` - `close`
-Berikut adalah contoh program menggunakan `for` - `range` untuk menerima data dari channel.
+Berikut adalah contoh program pengaplikasian `for`, `range`, dan `close` untuk penerimaan data dari channel.
-Ok, pertama siapkan fungsi `sendMessage()` yang tugasnya mengirim data via channel. Di dalam fungsi ini dijalankan perulangan sebanyak 20 kali, ditiap perulangannya data dikirim ke channel. Channel di-close setelah semua data selesai dikirim.
+Pertama siapkan fungsi `sendMessage()` yang tugasnya mengirim data via channel. Di dalam fungsi ini dijalankan perulangan sebanyak 20 kali, ditiap perulangannya data dikirim ke channel. Channel di-close setelah semua data selesai dikirim.
```go
func sendMessage(ch chan<- string) {
@@ -21,7 +22,7 @@ func sendMessage(ch chan<- string) {
}
```
-Siapkan juga fungsi `printMessage()` untuk handle penerimaan data. Di dalam fungsi tersebut, channel di-looping menggunakan `for` - `range`. Di tiap looping, data yang diterima dari channel ditampilkan.
+Siapkan juga fungsi `printMessage()` untuk handle penerimaan data. Di dalam fungsi tersebut, channel di-looping menggunakan `for` - `range`. Di setiap iterasi, data yang diterima dari channel ditampilkan.
```go
func printMessage(ch <-chan string) {
@@ -31,7 +32,7 @@ func printMessage(ch <-chan string) {
}
```
-Buat channel baru dalam fungsi `main()`, jalankan `sendMessage()` sebagai goroutine (dengan ini 20 data yang berada dalam fungsi tersebut dikirimkan via goroutine baru). Tak lupa jalankan juga fungsi `printMessage()`.
+Selanjutnya, buat channel baru dalam fungsi `main()`, jalankan `sendMessage()` sebagai goroutine. Dengan ini 20 data yang berada dalam fungsi tersebut dikirimkan via goroutine baru. Tak lupa jalankan juga fungsi `printMessage()`.
```go
func main() {
@@ -43,25 +44,25 @@ func main() {
}
```
-Setelah 20 data sukses dikirim dan diterima, channel `ch` di-non-aktifkan (`close(ch)`). Membuat perulangan data channel dalam `printMessage()` juga akan berhenti.
+Setelah 20 data yang dikirim sukses diterima, channel `ch` di-non-aktifkan dengan adanya statement `close(ch)`. Statement tersebut menghentikan perulangan channel dalam `printMessage()`.
![Penerapan for-range-close pada channel](images/A_channel_range_close_1_for_range_close.png)
----
+## A.34.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai channel.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.34.1.1. Channel Direction
+#### ◉ Channel Direction
-Ada yang unik dengan fitur parameter channel yang disediakan oleh Go. Level akses channel bisa ditentukan, apakah hanya sebagai penerima, pengirim, atau penerima sekaligus pengirim. Konsep ini disebut dengan **channel direction**.
+Go mendesain API channel untuk mendukung level akses channel, apakah hanya sebagai penerima, pengirim, atau penerima sekaligus pengirim. Konsep ini disebut dengan **channel direction**.
Cara pemberian level akses adalah dengan menambahkan tanda `<-` sebelum atau setelah keyword `chan`. Untuk lebih jelasnya bisa dilihat di list berikut.
| Sintaks | Penjelasan |
| :------- | :--------- |
-| `ch chan string` | Parameter `ch` bisa digunakan untuk **mengirim** dan **menerima** data |
-| `ch chan<- string` | Parameter `ch` hanya bisa digunakan untuk **mengirim** data |
-| `ch <-chan string` | Parameter `ch` hanya bisa digunakan untuk **menerima** data |
+| `ch chan string` | Parameter `ch` untuk **mengirim** dan **menerima** data |
+| `ch chan<- string` | Parameter `ch` hanya untuk **mengirim** data |
+| `ch <-chan string` | Parameter `ch` hanya untuk **menerima** data |
Pada kode di atas bisa dilihat bahwa secara default channel akan memiliki kemampuan untuk mengirim dan menerima data. Untuk mengubah channel tersebut agar hanya bisa mengirim atau menerima saja, dengan memanfaatkan simbol `<-`.
diff --git a/content/A-channel-select.md b/content/A-channel-select.md
index 3f45edaf0..07b9603bd 100644
--- a/content/A-channel-select.md
+++ b/content/A-channel-select.md
@@ -1,16 +1,16 @@
# A.33. Channel - Select
-Disediakannya channel membuat engineer menjadi mudah dalam me-manage goroutine. Namun perlu di-ingat, meskipun lewat channel manajemen goroutine menjadi mudah, fungsi utama channel bukan untuk kontrol, melainkan untuk sharing data antar goroutine.
+Channel membuat manajemen goroutine menjadi sangat mudah di Go. Namun perlu di-ingat, fungsi utama channel adalah bukan untuk kontrol eksekusi goroutine, melainkan untuk sharing data atau komunikasi goroutine.
-> Nantinya pada chapter [A.59. sync.WaitGroup](/A-waitgroup.html) akan dibahas secara komprehensif bagaimana cara optimal mengontrol goroutine.
+> Pada chapter [A.59. sync.WaitGroup](/A-waitgroup.html) akan dibahas secara komprehensif tentang cara yang lebih optimal untuk kontrol eksekusi goroutine.
-Ada kalanya kita butuh tak hanya satu channel saja untuk melakukan komunikasi data antar goroutine. Tergantung jenis kasusnya, sangat mungkin lebih dari satu channel dibutuhkan. Nah, di situlah kegunaan dari `select`. Select ini mempermudah kontrol komunikasi data lewat satu ataupun banyak channel.
+Tergantung jenis kasusnya, ada kalanya kita butuh lebih dari satu channel untuk komunikasi data antar goroutine. Penerimaan data pada banyak goroutine penerapannya masih sama, yaitu dengan menambahkan karakter `<-` pada statement. Selain itu, ada juga cara lain yaitu menggunakan keyword `select`. Keyword ini mempermudah kontrol penerimaan data via satu atau lebih dari satu channel.
Cara penggunaan `select` untuk kontrol channel sama seperti penggunaan `switch` untuk seleksi kondisi.
## A.33.1. Penerapan Keyword `select`
-Program berikut merupakan contoh sederhana penerapan select dalam channel. Dipersiapkan 2 buah goroutine, satu untuk pencarian rata-rata, dan satu untuk nilai tertinggi. Hasil operasi di masing-masing goroutine dikirimkan ke fungsi `main()` via channel (ada dua channel). Di fungsi `main()` sendiri, data tersebut diterima dengan memanfaatkan keyword `select`.
+Program berikut merupakan contoh sederhana penerapan keyword `select`. Di sini disiapkan 2 buah goroutine, satu untuk menghitung rata-rata dari data array numerik, dan satu lagi untuk pencarian nilai tertinggi. Hasil operasi di masing-masing goroutine dikirimkan ke fungsi `main()` via channel (ada dua channel). Di fungsi `main()` sendiri, data tersebut diterima dengan memanfaatkan keyword `select`.
Ok, langsung saja kita praktek. Pertama, siapkan 2 fungsi yang sudah dibahas di atas. Fungsi pertama digunakan untuk mencari rata-rata, dan fungsi kedua untuk penentuan nilai tertinggi dari sebuah slice.
diff --git a/content/A-channel-timeout.md b/content/A-channel-timeout.md
index aaa154359..a76d38bae 100644
--- a/content/A-channel-timeout.md
+++ b/content/A-channel-timeout.md
@@ -1,12 +1,12 @@
# A.35. Channel - Timeout
-Teknik channel timeout digunakan untuk mengontrol penerimaan data dari channel mengacu ke kapan waktu diterimanya data, dengan durasi timeout bisa kita tentukan sendiri.
+Teknik channel timeout digunakan untuk kontrol waktu penerimaan data pada channel, berapa lama channel tersebut harus menunggu hingga akhirnya suatu penerimaan data dianggap timeout.
-Ketika tidak ada aktivitas penerimaan data dalam durasi yang sudah ditentukan, maka blok timeout dieksekusi.
+Durasi penerimaan kita tentukan sendiri. Ketika tidak ada aktivitas penerimaan data dalam durasi tersebut, blok timeout dijalankan.
## A.35.1. Penerapan Channel Timeout
-Berikut adalah program sederhana contoh pengaplikasian timeout pada channel. Sebuah goroutine baru dijalankan dengan tugas mengirimkan data setiap interval tertentu, dengan durasi interval-nya sendiri adalah acak/random.
+Berikut adalah program sederhana contoh pengaplikasian timeout pada channel. Sebuah goroutine dijalankan dengan tugas adalah mengirimkan data secara berulang dalam interval tertentu, dengan durasi interval-nya sendiri adalah acak/random.
```go
package main
@@ -45,8 +45,8 @@ func retreiveData(ch <-chan int) {
Ada 2 blok kondisi pada `select` tersebut.
- - Kondisi `case data := <-messages:`, akan terpenuhi ketika ada serah terima data pada channel `messages`.
- - Kondisi `case <-time.After(time.Second * 5):`, akan terpenuhi ketika tidak ada aktivitas penerimaan data dari channel dalam durasi 5 detik. Blok inilah yang kita sebut sebagai blok timeout.
+- Kondisi `case data := <-messages:`, akan terpenuhi ketika ada serah terima data pada channel `messages`.
+- Kondisi `case <-time.After(time.Second * 5):`, akan terpenuhi ketika tidak ada aktivitas penerimaan data dari channel dalam durasi 5 detik. Blok inilah yang kita sebut sebagai blok timeout.
Terakhir, kedua fungsi tersebut dipanggil di `main()`.
@@ -61,7 +61,7 @@ func main() {
}
```
-Muncul output setiap kali ada penerimaan data dengan delay waktu acak. Ketika tidak ada aktivitas penerimaan dari channel dalam durasi 5 detik, perulangan pengecekkan channel diberhentikan.
+Muncul output setiap kali ada penerimaan data dengan delay waktu acak. Ketika dalam durasi 5 detik tidak ada aktivitas penerimaan sama sekali, maka dianggap timeout dan perulangan pengecekkan channel dihentikan.
![Channel timeout](images/A_channel_timeout_1_channel_delay.png)
diff --git a/content/A-channel.md b/content/A-channel.md
index 1a65b1e28..583ada27a 100644
--- a/content/A-channel.md
+++ b/content/A-channel.md
@@ -1,16 +1,18 @@
# A.31. Channel
-**Channel** digunakan untuk menghubungkan goroutine satu dengan goroutine lain. Mekanisme yang dilakukan adalah serah-terima data lewat channel tersebut. Dalam komunikasinya, sebuah channel difungsikan sebagai pengirim di sebuah goroutine, dan juga sebagai penerima di goroutine lainnya. Pengiriman dan penerimaan data pada channel bersifat **blocking** atau **synchronous**.
+**Channel** digunakan untuk menghubungkan goroutine satu dengan goroutine lain dengan mekanisme serah terima data, jadi harus ada data yang dikirim dari goroutine A untuk kemudian diterima di goroutine B.
-![Analogi channel](images/A_channel_1_analogy.png)
+Peran channel adalah sebagai media perantara bagi pengirim data dan juga penerima data. Jadi channel adalah *thread safe*, aman digunakan di banyak goroutine.
+
+Pengiriman dan penerimaan data pada channel bersifat **blocking** atau **synchronous**. Artinya, statement di-bawah syntax pengiriman dan penerimaan data via channel hanya akan dieksekusi setelah proses serah terima berlangsung dan selesai.
-Pada chapter ini kita akan belajar mengenai pemanfaatan channel.
+![Analogi channel](images/A_channel_1_analogy.png)
## A.31.1. Penerapan Channel
-Channel merupakan sebuah variabel, dibuat dengan menggunakan kombinasi keyword `make` dan `chan`. Variabel channel memiliki satu tugas, menjadi pengirim, atau penerima data.
+Channel berbentuk variabel, dibuat dengan menggunakan kombinasi keyword `make` dan `chan`.
-Program berikut adalah contoh implementasi channel. 3 buah goroutine dieksekusi, di masing-masing goroutine terdapat proses pengiriman data lewat channel. Data tersebut akan diterima 3 kali di goroutine utama `main`.
+Program berikut adalah contoh implementasi channel. 3 buah goroutine dieksekusi, di masing-masing goroutine terdapat proses pengiriman data lewat channel. Kesemua data tersebut nantinya diterima oleh di goroutine utama yaitu proses yang dijalankan di dalam blok fungsi `main()`.
```go
package main
@@ -43,7 +45,7 @@ func main() {
}
```
-Pada kode di atas, variabel `messages` dideklarasikan bertipe channel `string`. Cara pembuatan channel yaitu dengan menuliskan keyword `make` dengan isi keyword `chan` diikuti dengan tipe data channel yang diinginkan.
+Pada kode di atas, variabel `messages` dideklarasikan bertipe channel `string`. Contoh cara pembuatan channel bisa dilihat di situ, yaitu dengan memanggil fungsi `make()` dengan isi adalah keyword `chan` diikuti dengan tipe data channel yang diinginkan.
```go
var messages = make(chan string)
@@ -75,21 +77,24 @@ var message1 = <-messages
fmt.Println(message1)
```
-Penerimaan channel bersifat blocking. Artinya statement `var message1 = <-messages` hingga setelahnya tidak akan dieksekusi sebelum ada data yang dikirim lewat channel.
+Penerimaan channel bersifat blocking. Artinya:
-Ke semua data yang dikirim dari tiga goroutine berbeda tersebut datanya akan diterima secara berurutan oleh `message1`, `message2`, `message3`; untuk kemudian ditampilkan.
+- Statement `var message1 = <-messages` hingga setelahnya tidak akan dieksekusi sebelum ada data yang dikirim lewat channel.
+- Berlaku juga dengan statement `messages <- data`. Statement dibawahnya tidak akan dieksekusi hingga data yang dikirim ke channel `messages` benar-benar diterima oleh penerima, yaitu variabel `message1`.
+
+Ke semua data yang dikirim dari tiga goroutine berbeda tersebut nantinya diterima oleh `message1`, `message2`, `message3`; untuk kemudian ditampilkan.
![Implementasi channel](images/A_channel_2_channel.png)
Dari screenshot output di atas bisa dilihat bahwa text yang dikembalikan oleh `sayHelloTo` tidak selalu berurutan, meskipun penerimaan datanya adalah berurutan. Hal ini dikarenakan, pengiriman data adalah dari 3 goroutine yang berbeda, yang kita tidak tau mana yang prosesnya selesai lebih dulu. Goroutine yang dieksekusi lebih awal belum tentu selesai lebih awal, yang jelas proses yang selesai lebih awal datanya akan diterima lebih awal.
-Karena pengiriman dan penerimaan data lewat channel bersifat **blocking**, tidak perlu memanfaatkan sifat blocking dari fungsi sejenis `fmt.Scanln()` atau lainnya, untuk mengantisipasi goroutine utama `main` selesai sebelum ketiga goroutine di atas selesai.
+Karena pengiriman dan penerimaan data lewat channel bersifat **blocking**, tidak perlu memanfaatkan sifat blocking dari fungsi seperti `fmt.Scanln()` (atau lainnya) untuk mengantisipasi goroutine utama (yaitu `main`) selesai sebelum ketiga goroutine di atas selesai.
## A.31.2. Channel Sebagai Tipe Data Parameter
-Variabel channel bisa di-pass ke fungsi lain sebagai parameter. Cukup tambahkan keyword `chan` pada deklarasi parameter agar operasi pass channel variabel bisa dilakukan.
+Variabel channel bisa di-pass ke fungsi lain via parameter. Cukup tambahkan keyword `chan` pada deklarasi parameter agar operasi pass channel variabel bisa dilakukan.
-Siapkan fungsi `printMessage` dengan parameter adalah channel. Lalu ambil data yang dikirimkan lewat channel tersebut untuk ditampilkan.
+Siapkan fungsi `printMessage()` dengan parameter adalah channel. Lalu ambil data yang dikirimkan lewat channel tersebut untuk ditampilkan.
```go
func printMessage(what chan string) {
@@ -97,7 +102,7 @@ func printMessage(what chan string) {
}
```
-Setelah itu ubah implementasi di fungsi `main`.
+Setelah itu ubah implementasi di fungsi `main()`.
```go
func main() {
@@ -120,17 +125,17 @@ func main() {
Output program di atas sama dengan program sebelumnya.
-Parameter `what` fungsi `printMessage` bertipe channel `string`, bisa dilihat dari kode `chan string` pada cara deklarasinya. Operasi serah-terima data akan bisa dilakukan pada variabel tersebut, dan akan berdampak juga pada variabel `messages` di fungsi `main`.
+Parameter `what` pada fungsi `printMessage()` bertipe channel `string`, bisa dilihat dari kode `chan string` pada cara deklarasinya. Operasi serah-terima data akan bisa dilakukan pada variabel tersebut, dan akan berdampak juga pada variabel `messages` di fungsi `main()`.
Passing data bertipe channel lewat parameter sifatnya **pass by reference**, yang di transferkan adalah pointer datanya, bukan nilai datanya.
![Parameter channel](images/A_channel_3_channel_param.png)
----
+## A.32.3. Penjelasan tambahan
-Berikut merupakan penjelasan tambahan untuk kode di atas.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
+#### ◉ Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
Data slice yang baru di inisialisasi bisa langsung di-iterasi, caranya mudah dengan menuliskannya langsung setelah keyword `range`.
@@ -140,7 +145,7 @@ for _, each := range []string{"wick", "hunt", "bourne"} {
}
```
-#### • Eksekusi Goroutine Pada IIFE
+#### ◉ Eksekusi Goroutine Pada IIFE
Eksekusi goroutine tidak harus pada fungsi atau closure yang sudah terdefinisi. Sebuah IIFE juga bisa dijalankan sebagai goroutine baru. Caranya dengan langsung menambahkan keyword `go` pada waktu deklarasi-eksekusi IIFE-nya.
diff --git a/content/A-client-http-request-simple.md b/content/A-client-http-request-simple.md
index d175f667b..91b737d3f 100644
--- a/content/A-client-http-request-simple.md
+++ b/content/A-client-http-request-simple.md
@@ -1,6 +1,6 @@
# A.55. Simple Client HTTP Request
-Pada chapter sebelumnya telah dibahas bagaimana membuat Web Service API yang mem-provide data JSON, pada chapter ini kita akan belajar mengenai cara untuk mengkonsumsi data tersebut.
+Pada chapter sebelumnya telah dibahas bagaimana cara membuat Web Service API yang response data-nya berbentuk JSON. Pada chapter ini kita akan belajar mengenai cara untuk mengkonsumsi data tersebut.
Pastikan anda sudah mempraktekkan apa-apa yang ada pada chapter sebelumnya ([A.54. Web Service API Server](/A-web-service-api.html)), karena web service yang telah dibuat di situ juga dipergunakan pada chapter ini.
@@ -8,9 +8,9 @@ Pastikan anda sudah mempraktekkan apa-apa yang ada pada chapter sebelumnya ([A.5
## A.55.1. Penggunaan HTTP Request
-Package `net/http`, selain berisikan tools untuk keperluan pembuatan web, juga berisikan fungsi-fungsi untuk melakukan http request. Salah satunya adalah `http.NewRequest()` yang akan kita bahas di sini.
+Package `net/http`, selain berisikan tools untuk keperluan pembuatan web, juga berisikan fungsi-fungsi untuk melakukan http request. Salah satunya adalah `http.NewRequest()` yang akan kita bahas di sini. Untuk menggunakannya pastikan import package-nya terlebih dahulu.
-Sebelumnya, import package yang dibutuhkan. Dan siapkan struct `student` yang nantinya akan dipakai sebagai tipe data reponse dari web API. Struk tersebut skema nya sama dengan yang ada pada chapter ([A.54. Web Service API Server](/A-web-service-api.html)).
+Kemudian siapkan struct `student` yang nantinya akan dipakai sebagai tipe data reponse dari web API. Struk tersebut skema nya sama dengan yang ada pada chapter ([A.54. Web Service API Server](/A-web-service-api.html)).
```go
package main
@@ -60,19 +60,19 @@ Statement `&http.Client{}` menghasilkan instance `http.Client`. Objek ini nantin
Fungsi `http.NewRequest()` digunakan untuk membuat request baru. Fungsi tersebut memiliki 3 parameter yang wajib diisi.
- 1. Parameter pertama, berisikan tipe request **POST** atau **GET** atau lainnya
- 2. Parameter kedua, adalah URL tujuan request
- 3. Parameter ketiga, form data request (jika ada)
+1. Parameter pertama, berisikan tipe request **POST** atau **GET** atau lainnya
+2. Parameter kedua, adalah URL tujuan request
+3. Parameter ketiga, form data request (jika ada)
-Fungsi tersebut menghasilkan instance bertipe `http.Request`. Objek tersebut nantinya disisipkan pada saat eksekusi request.
+Fungsi tersebut menghasilkan instance bertipe `http.Request` yang nantinya digunakan saat eksekusi request.
-Cara eksekusi request sendiri adalah dengan memanggil method `Do()` pada instance `http.Client` yang sudah dibuat, dengan parameter adalah instance request-nya. Contohnya seperti pada `client.Do(request)`.
+Cara eksekusi request sendiri adalah dengan memanggil method `Do()` pada variabel `client` yang sudah dibuat. Fungsi `Do()` dipanggil dengan disisipkan argument fungsi yaitu object `request`. Penulisannya: `client.Do(request)`.
-Method tersebut mengembalikan instance bertipe `http.Response`, yang di dalamnya berisikan informasi yang dikembalikan dari web API.
+Method tersebut mengembalikan instance bertipe `http.Response` yang di contoh ditampung oleh variabel `response`. Dari data response tersebut kita bisa mengakses informasi yang berhubungan dengan HTTP response, termasuk response body.
-Data response bisa diambil lewat property `Body` dalam bentuk string. Gunakan JSON Decoder untuk mengkonversinya menjadi bentuk JSON. Contohnya bisa dilihat di kode di atas, `json.NewDecoder(response.Body).Decode(&data)`. Setelah itu barulah kita bisa menampilkannya.
+Data response body tersedia via property `Body` dalam tipe `[]byte`. Gunakan JSON Decoder untuk mengkonversinya menjadi bentuk JSON. Contohnya bisa dilihat di kode di atas, `json.NewDecoder(response.Body).Decode(&data)`.
-Perlu diketahui, data response perlu di-**close** setelah tidak dipakai. Caranya seperti pada kode `defer response.Body.Close()`.
+Perlu diketahui, data response perlu di-**close** setelah tidak dipakai. Caranya dengan memanggil method `Close()` milik property `Body` yang dalam penerapannya umumnya di-defer. Contohnya: `defer response.Body.Close()`.
Selanjutnya, eksekusi fungsi `fetchUsers()` dalam fungsi `main()`.
@@ -90,20 +90,20 @@ func main() {
}
```
-Ok, terakhir sebelum memulai testing, pastikan telah run aplikasi pada chapter sebelumya ([A.54. Web Service API Server](/A-web-service-api.html)). Kemudian start prompt cmd/terminal baru dan jalankan program yang barusan kita buat pada chapter ini.
+Ok, terakhir sebelum memulai testing, pastikan telah run aplikasi pada chapter sebelumya ([A.54. Web Service API Server](/A-web-service-api.html)). Setelah itu start prompt cmd/terminal baru dan jalankan program yang telah dibuat di chapter ini.
![HTTP Request](images/A_http_request_1_http_request.png)
## A.55.2. HTTP Request Dengan Form Data
-Untuk menyisipkan data pada sebuah request, ada beberapa hal yang perlu ditambahkan. Yang pertama, import beberapa package lagi, `bytes` dan `net/url`.
+Untuk menyisipkan data pada sebuah request, ada beberapa hal yang perlu ditambahkan. Pertama, import package `bytes` dan `net/url`.
```go
import "bytes"
import "net/url"
```
-Buat fungsi baru, isinya request ke [http://localhost:8080/user](http://localhost:8080/user) dengan data yang disisipkan adalah `ID`.
+Kemudian buat fungsi baru, isinya request ke [http://localhost:8080/user](http://localhost:8080/user) dengan data yang disisipkan adalah `ID`.
```go
func fetchUser(ID string) (student, error) {
@@ -136,13 +136,13 @@ func fetchUser(ID string) (student, error) {
}
```
-Isi fungsi di atas bisa dilihat memiliki beberapa kemiripan dengan fungsi `fetchUsers()` sebelumnya.
+Isi fungsi `fetchUser()` memiliki beberapa kemiripan dengan fungsi `fetchUsers()` sebelumnya.
Statement `url.Values{}` akan menghasilkan objek yang nantinya digunakan sebagai form data request. Pada objek tersebut perlu di set data apa saja yang ingin dikirimkan menggunakan fungsi `Set()` seperti pada `param.Set("id", ID)`.
-Statement `bytes.NewBufferString(param.Encode())` maksudnya, objek form data di-encode lalu diubah menjadi bentuk `bytes.Buffer`, yang nantinya disisipkan pada parameter ketiga pemanggilan fungsi `http.NewRequest()`.
+Statement `bytes.NewBufferString(param.Encode())` melakukan proses encoding pada data param untuk kemudian diubah menjadi bentuk `bytes.Buffer`. Nantinya data buffer tersebut disisipkan pada parameter ketiga pemanggilan fungsi `http.NewRequest()`.
-Karena data yang akan dikirim di-encode, maka pada header perlu di set tipe konten request-nya. Kode `request.Header.Set("Content-Type", "application/x-www-form-urlencoded")` artinya tipe konten request di set sebagai `application/x-www-form-urlencoded`.
+Karena data yang akan dikirim adalah *encoded*, maka pada header perlu dituliskan juga tipe encoding-nya. Kode `request.Header.Set("Content-Type", "application/x-www-form-urlencoded")` menandai bahwa HTTP request berisi body yang ter-encode sesuai spesifikasi `application/x-www-form-urlencoded`.
> Pada konteks HTML, HTTP Request yang di trigger dari tag `
` secara default tipe konten-nya sudah di set `application/x-www-form-urlencoded`. Lebih detailnya bisa merujuk ke spesifikasi HTML form [http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1)
@@ -168,7 +168,7 @@ Untuk keperluan testing, kita hardcode `ID` nilainya `"E001"`. Jalankan program
## A.55.3. Secure & Insecure HTTP Request
-Kita telah mempelajari bagaimana cara membuat http request sederhana untuk kirim data dan juga ambil data. Nantinya pada chapter [C.27. Secure & Insecure Client HTTP Request](/C-secure-insecure-client-http-request.html) kita akan belajar cara membuat http client request yang lebih *njlimet* untuk kasus yang lebih advance, tapi sabar dulu hehe.
+Sampai sini kita telah belajar bagaimana cara membuat http request sederhana untuk kirim data dan juga ambil data. Nantinya pada chapter [C.27. Secure & Insecure Client HTTP Request](/C-secure-insecure-client-http-request.html) pembelajaran topik HTTP request dilanjutkan kembali, kita akan bahas tentang aspek keamanan/security suatu HTTP request.
---
diff --git a/content/A-command-line-args-flag.md b/content/A-command-line-args-flag.md
index 1fea223a4..d79470285 100644
--- a/content/A-command-line-args-flag.md
+++ b/content/A-command-line-args-flag.md
@@ -1,14 +1,12 @@
# A.48. Arguments & Flag
-**Arguments** adalah data opsional yang disisipkan ketika eksekusi program. Sedangkan **flag** merupakan ekstensi dari argument. Dengan flag, penulisan argument menjadi lebih rapi dan terstruktur.
+**Arguments** adalah data argument opsional yang disisipkan ketika eksekusi program. Sedangkan **flag** merupakan ekstensi dari argument. Dengan flag, penulisan argument menjadi lebih rapi dan terstruktur.
-Pada chapter ini kita akan belajar tentang penggunaan arguments dan flag.
+Pada chapter ini kita akan belajar tentang penerapan arguments dan flag.
## A.48.1. Penggunaan Arguments
-Data arguments bisa didapat lewat variabel `os.Args` (package `os` perlu di-import terlebih dahulu). Data tersebut tersimpan dalam bentuk array dengan pemisah adalah tanda spasi.
-
-Berikut merupakan contoh penggunaannya.
+Data arguments bisa didapat lewat variabel `os.Args` (package `os` perlu di-import terlebih dahulu). Data tersebut tersimpan dalam bentuk array. Setiap data argument yang disisipkan saat pemanggilan program, datanya dipecah menggunakan karakter spasi lalu di-map ke bentuk array. Contoh penerapan:
```go
package main
@@ -27,9 +25,7 @@ func main() {
}
```
-Pada saat eksekusi program disisipkan juga argument-nya. Sebagai contoh disisipkan 3 buah data sebagai argumen, yaitu: `banana`, `potato`, dan `ice cream`.
-
-Untuk eksekusinya sendiri bisa menggunakan `go run` ataupun dengan cara build-execute.
+Argument disisipkan saat eksekusi program. Sebagai contoh, kita ingin menyisipkan 3 buah argumen berikut: `banana`, `potato`, dan `ice cream`. Maka penulisan saat pemanggilan program-nya seperti ini:
- Menggunakan `go run`
@@ -44,11 +40,13 @@ Untuk eksekusinya sendiri bisa menggunakan `go run` ataupun dengan cara build-ex
$ ./bab45 banana potato "ice cream"
```
-Variabel `os.Args` mengembalikan tak hanya arguments saja, tapi juga path file executable (jika eksekusi-nya menggunakan `go run` maka path akan merujuk ke folder temporary). Gunakan `os.Args[1:]` untuk mengambil slice arguments-nya saja.
+Output program:
![Pemanfaatan arguments](images/A_cli_flag_arg_1_argument.png)
-Bisa dilihat pada kode di atas, bahwa untuk data argumen yang ada karakter spasi nya (
), maka harus diapit tanda petik (`"`), agar tidak dideteksi sebagai 2 argumen.
+Bisa dilihat pada kode di atas, bahwa untuk data argumen yang ada karakter spasi-nya (
) harus dituliskan dengan diapit tanda petik (`"`) agar tidak dideteksi sebagai 2 argumen.
+
+Variabel `os.Args` mengembalikan tak hanya arguments saja, tapi juga path file executable (jika eksekusi-nya menggunakan `go run` maka path akan merujuk ke folder temporary). Maka disini penting untuk hanya mengambil element index ke 1 hingga seterusnya saja via statement `os.Args[1:]`.
## A.48.2. Penggunaan Flag
@@ -85,7 +83,7 @@ fmt.Println(*dataName)
Kode tersebut maksudnya adalah, disiapkan flag bertipe `string`, dengan key adalah `name`, dengan nilai default `"anonymous"`, dan keterangan `"type your name"`. Nilai flag nya sendiri akan disimpan ke dalam variabel `dataName`.
-Nilai balik fungsi `flag.String()` adalah string pointer, jadi perlu di-*dereference* terlebih dahulu agar bisa mendapatkan nilai aslinya (`*dataName`).
+Nilai balik fungsi `flag.String()` adalah string pointer, jadi perlu di-*dereference* terlebih dahulu untuk mengakses nilai aslinya (`*dataName`).
![Contoh penggunaan flag](images/A_cli_flag_arg_2_flag.png)
@@ -110,7 +108,7 @@ Sebenarnya ada 2 cara deklarasi flag yang bisa digunakan, dan cara di atas merup
Cara kedua mirip dengan cara pertama, perbedannya adalah kalau di cara pertama nilai pointer flag dikembalikan lalu ditampung variabel. Sedangkan pada cara kedua, nilainya diambil lewat parameter pointer.
-Agar lebih jelas perhatikan contoh berikut.
+Agar lebih jelas perhatikan contoh berikut:
```go
// cara ke-1
diff --git a/content/A-concurrency-pipeline.md b/content/A-concurrency-pipeline.md
index 4b335c40e..d842a7649 100644
--- a/content/A-concurrency-pipeline.md
+++ b/content/A-concurrency-pipeline.md
@@ -1,14 +1,14 @@
# A.62. Concurrency Pattern: Pipeline
-Kita sudah membahas beberapa kali tentang *concurrency* atau konkurensi di Go programming. Pada chapter ini kita akan belajar salah satu best practice konkurensi dalam Go, yaitu *pipeline*, yang merupakan satu di antara banyak *concurrency pattern* yang ada di Go.
+Kita sudah membahas beberapa kali tentang topik *concurrency* atau konkurensi di Go programming. Pada chapter ini kita akan belajar salah satu best practice konkurensi dalam Go, yaitu teknik *pipeline*, yang merupakan satu di antara banyak *concurrency pattern* yang ada di Go.
-Go memiliki beberapa API untuk keperluan konkurensi, di antara *goroutine* dan *channel*. Dengan memanfaatkan APIs yang ada kita bisa membentuk sebuah *streaming data pipeline* yang benefitnya adalah efisiensi penggunaan I/O dan efisiensi penggunaan banyak CPU.
+Go memiliki beberapa API untuk keperluan konkurensi, dua diantaranya adalah *goroutine* dan *channel*. Dengan memanfaatkan APIs yang ada kita bisa membentuk sebuah *streaming data pipeline* yang benefitnya adalah efisiensi penggunaan I/O dan efisiensi penggunaan banyak CPU.
## A.62.1. Konsep *Pipeline*
Definisi *pipeline* yang paling mudah versi penulis adalah **beberapa/banyak proses yang berjalan secara konkuren yang masing-masing proses merupakan bagian dari serangkaian tahapan proses yang berhubungan satu sama lain**.
-Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang di mana database yang di backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut:
+Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang di mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut:
1. Kita perlu data *list* dari semua database yang harus di-backup, beserta alamat akses dan kredensial-nya.
2. Kita jalankan proses backup, bisa secara sekuensial (setelah `db1` selesai, lanjut `db2`, lanjut `db3`, dst), atau secara paralel (proses backup `db1`, `db2`, `db3`, dan lainnya dijalankan secara bersamaan).
@@ -18,7 +18,7 @@ Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database
* B. File-file hasil dump kemudian di-*archive* ke bentuk `.zip` atau `.tar.gz` (misalnya).
* C. File archive di-kirim ke server backup, sebagai contoh AWS S3.
-Kalau diperhatikan pada kasus di atas, mungkin akan lebih bagus dari segi performansi kalau proses backup banyak database tersebut dilakukan secara parallel. Dan untuk ini penulis setuju.
+Kalau diperhatikan pada kasus di atas, mungkin akan lebih bagus dari segi performansi kalau proses backup banyak database tersebut dilakukan secara parallel.
Dan akan lebih bagus lagi, jika di masing-masing proses backup database tersebut, proses A, B, dan C dijalankan secara konkuren. Dengan menjadikan ketiga proses tersebut (A, B, C) sebagai proses konkuren, maka I/O akan lebih efisien. Nantinya antara proses A, B, dan C eksekusinya akan tetap berurutan (karena memang harus berjalan secara urut. Tidak boleh kalau misal B lebih dulu dieksekusi kemudian A); akan tetapi, ketika goroutine yang bertanggung jawab untuk eksekusi proses A selesai, kita bisa lanjut dengan eksekusi proses B (yang memang *next stage*-nya proses A) plus eksekusi proses A lainnya (database lain) secara paralel. Jadi goroutine yang handle A ini ga sampai menganggur.
@@ -47,14 +47,14 @@ Untuk mempermudah memahami tabel di atas silakan ikuti penjelasan beruntun berik
Pada contoh ini kita asumsikan pipeline A adalah hanya satu goroutine, pipeline B juga satu goroutine, demikian juga pipeline C. Tapi sebenarnya dalam implementasi *real world* bisa saja ada banyak goroutine untuk masing-masing pipeline (banyak goroutine untuk pipeline A, banyak goroutine untuk pipeline B, banyak goroutine untuk pipeline C).
-Semoga cukup jelas ya. Gpp kalau bingung, nanti kita sambil praktek juga jadi bisa saja temen-temen mulai benar-benar pahamnya setelah praktek.
+Semoga cukup jelas ya. Tapi jika masih bingung, juga tidak apa. Kita sambil praktek juga, dan bisa saja pembaca mulai benar-benar pahamnya saat praktek.
+
+> Penulis sarankan untuk benar-benar memahami setiap bagian praktek ini, karena topik ini merupakan pembahasan yang cukup berat untuk pemula, tapi masih dalam klasifikasi fundamental kalau di Go programming. Bingung tidak apa, nanti bisa di-ulang-ulang, yang penting tidak sekadar *copy-paste*.
## A.62.2. Skenario Praktek
Ok, penjabaran teori sepanjang sungai `nil` tidak akan banyak membawa penjelasan yang real kalau tidak diiringi dengan praktek. So, mari kita mulai praktek.
-Penulis sarankan untuk benar-benar memahami setiap bagian praktek ini, karena topik ini merupakan pembahasan yang cukup berat untuk pemula, tapi masih dalam klasifikasi fundamental kalau di Go programming. Bingung tidak apa, nanti bisa di-ulang-ulang, yang penting tidak sekadar *copy-paste*.
-
Untuk skenario praktek kita tidak menggunakan analogi backup database di atas ya, karena untuk setup environment-nya butuh banyak *effort*. Skenario praktek yang kita pakai adalah mencari [md5 sum](https://en.wikipedia.org/wiki/Md5sum) dari banyak file, kemudian menggunakan hash dari content-nya sebagai nama file. Jadi file yang lama akan di-rename dengan nama baru yaitu hash dari konten file tersebut.
Agar skenario ini bisa kita eksekusi, kita perlu siapkan dulu sebuah program untuk *generate dummy files*.
@@ -63,7 +63,7 @@ Agar skenario ini bisa kita eksekusi, kita perlu siapkan dulu sebuah program unt
Buat project baru dengan nama bebas loss gak reweellll beserta satu buah file bernama `1-dummy-file-generator.go`.
-Dalam file tersebut import beberapa hal dan definisikan, yaitu:
+Dalam file tersebut import dan definisikan beberapa hal, diantaranya:
1. Konstanta `totalFile` yang isinya jumlah file yang ingin di-generate.
1. Variabel `contentLength` yang isinya panjang karakter random yang merupakan isi dari masing-masing *generated* file.
@@ -87,7 +87,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.59-pipeline-temp")
```
-Kemudian siapkan fungsi `main()` yang isinya statement pemanggilan fungsi `generate()`. Selain itu juga ada beberapa statement untuk keperluan *benchmark* performa dari sisi *execution time*.
+Kemudian siapkan fungsi `main()` yang isinya statement pemanggilan fungsi `generate()`, dan beberapa hal lainnya untuk keperluan *benchmark* performa dari sisi *execution time*.
```go
func main() {
@@ -117,7 +117,7 @@ func randomString(length int) string {
}
```
-Lalu siapkan fungsi `generateFiles()`-nya. isinya kurang lebih adalah generate banyak file sejumlah `totalFile`. Lalu di tiap-tiap file di-isi dengan *random string* dengan lebar sepanjang `contentLength`. Untuk nama file-nya sendiri, formatnya adalah `file-.txt`.
+Siapkan fungsi `generateFiles()`-nya, isinya kurang lebih adalah generate banyak file sejumlah `totalFile`. Lalu di tiap-tiap file di-isi dengan *random string* dengan lebar sepanjang `contentLength`. Untuk nama file-nya sendiri, formatnya adalah `file-.txt`.
```go
func generateFiles() {
@@ -151,9 +151,9 @@ Bisa dilihat sebanyak 3000 dummy file di-generate pada folder temporary os, di s
## A.62.4. Program 2: Baca Semua Files, Cari MD5 Hash-nya, Lalu Gunakan Hash Untuk Rename File
-Sesuai judul sub bagian, kita akan buat satu file program lagi, yang isinya kurang lebih adalah melakukan pembacaan terhadap semua dummy file yang sudah di-generate untuk kemudian dicari *hash*-nya, lalu menggunakan value hash tersebut sebagai nama file baru masing-masing file.
+Sesuai judul sub bagian, kita akan buat satu file program lagi, yang isinya adalah melakukan operasi baca terhadap semua dummy file yang sudah di-generate, untuk kemudian dicari *hash*-nya lalu menggunakan nilai hash tersebut sebagai nama untuk file-file baru yang akan dibuat.
-Pada bagian ini kita belum masuk ke aspek konkurensi-nya ya. Sabar dulu. Saya akan coba sampaikan dengan penjabaran yang bisa diterima oleh banyak pembaca termasuk yang masih junior.
+Pada bagian ini kita belum masuk ke aspek konkurensi-nya ya. Sabar dulu. Saya akan coba sampaikan dengan penjabaran yang bisa diterima oleh banyak pembaca (termasuk yang masih awam banget).
Siapkan file `2-find-md5-sum-of-file-then-rename-it.go`, import beberapa *packages* dan siapkan definisi variabel `tempPath`.
@@ -233,7 +233,7 @@ func proceed() {
}
```
-Cukup panjang isi fungsi ini, tetapi isinya cukup *straightforward* kok.
+Cukup panjang isi fungsi ini, tetapi isinya cukup *straight forward* kok.
* Pertama kita siapkan `counterTotal` sebagai counter jumlah file yang ditemukan dalam `$TEMP/chapter-A.59-pipeline-temp`. Idealnya jumlahnya adalah sama dengan isi variabel `totalFile` pada program pertama, kecuali ada error.
* Kedua, kita siapkan `counterRenamed` sebagai counter jumlah file yang berhasil di-rename. Untuk ini juga idealnya sama dengan nilai pada `counterTotal`, kecuali ada error
@@ -250,7 +250,7 @@ Selesai dalam waktu **1,17 detik**, lumayan untuk eksekusi proses sekuensial.
Ok, aplikasi sudah siap. Selanjutnya kita akan refactor aplikasi tersebut ke bentuk konkuren menggunakan metode *pipeline*.
-## A.62.5. Program 3: Lakukan Proses Secara Concurrent Menggunakan Pipeline
+## A.62.5. Program 3: Lakukan Proses Secara Concurrent Menggunakan Teknik Pipeline
Pada bagian ini kita akan re-write ulang program 2, isinya masih sama persis kalau dilihat dari perspektif bisnis logic, tapi metode yang kita terapkan dari sisi engineering berbeda. Di sini kita akan terapkan *pipeline*. Bisnis logic akan dipecah menjadi 3 dan seluruhnya dieksekusi secara konkuren, yaitu:
@@ -258,9 +258,9 @@ Pada bagian ini kita akan re-write ulang program 2, isinya masih sama persis kal
* Proses perhitungan md5 hash sum
* Proses rename file
-Kenapa kita pecah, karena ketiga proses tersebut bisa dijalankan bersama secara konkuren, dalam artian misalnya ketika file1 sudah selesai dibaca, perhitungan md5 sum nya bisa dijalankan secara bersama dengan pembacaan file2. Begitu juga untuk proses rename-nya, misalnya, proses rename file24 bisa dijalnkan secara konkuren bersamaan dengan proses hitung md5 sum file22 dan bersamaan dengan proses baca file28.
+Kenapa kita pecah, karena ketiga proses tersebut bisa dijalankan bersama secara konkuren, dalam artian misalnya ketika `file1` sudah selesai dibaca, perhitungan md5sum-nya bisa dijalankan secara bersama dengan pembacaan `file2`. Begitu juga untuk proses rename-nya, misalnya, proses rename `file24` bisa dijalnkan secara konkuren bersamaan dengan proses hitung md5sum `file22` dan bersamaan dengan proses baca `file28`.
-#### • Basis Kode Program
+#### ◉ Basis Kode Program
Mungkin agar lebih terlihat perbandingannya nanti di akhir, kita siapkan file terpisah saja untuk program ini. Siapkan file baru bernama `3-find-md5-sum-of-file-then-rename-it-concurrently.go`.
@@ -291,7 +291,7 @@ type FileInfo struct {
Kurang lebih sama seperti sebelumnya, hanya saja ada beberapa packages lain yg di-import dan ada struct `FileInfo`. Struct ini digunakan sebagai metadata tiap file. Karena nantinya proses read file, md5sum, dan rename file akan dipecah menjadi 3 goroutine berbeda, maka perlu ada metadata untuk mempermudah tracking file, agar nanti ketika dapat md5 sum nya tidak salah simpan, dan ketika rename tidak salah file.
-#### • Pipeline 1: Baca File
+#### ◉ Pipeline 1: Baca File
Siapkan fungsi main, lalu panggil fungsi `readFiles()`.
@@ -349,17 +349,17 @@ func readFiles() <-chan FileInfo {
}
```
-Bisa dilihat isi fungsi `readFiles()`. Di fungsi tersebut ada sebuah channel bernama `chanOut` tipenya channel `FileInfo`, yang variabel channel ini langsung dijadikan nilai balik dari fungsi `readFiles()`.
+Bisa dilihat isi fungsi `readFiles()`. Di fungsi tersebut ada sebuah channel bernama `chanOut` tipenya channel `FileInfo`, variabel channel ini dijadikan nilai balik dari fungsi `readFiles()`.
Di dalam fungsi `readFiles()` juga ada proses lain yang berjalan secara *asynchronous* dan *concurrent* yaitu goroutine yang isinya pembacaan file. Dalam blok kode baca file, informasi `path` dan konten file dibungkus dalam objek baru dengan tipe `FileInfo` kemudian dikirim ke channel `chanOut`.
-Karena proses utama dalam fungsi `readFiles` berada dalam goroutine, maka di `main()`, ketika statement `chanFileContent := readFiles()` selesai dieksekusi, bukan berarti proses pembacaan file selesai, malah mungkin baru saja dimulai. Ini karena proses baca file dijalankan dalam goroutine di dalam fungsi `readFiles()` tersebut.
+Karena proses utama dalam fungsi `readFiles()` berada dalam goroutine, maka di `main()`, ketika statement `chanFileContent := readFiles()` selesai dieksekusi, bukan berarti proses pembacaan file selesai, malah mungkin baru saja dimulai. Ini karena proses baca file dijalankan dalam goroutine di dalam fungsi `readFiles()` tersebut.
Mengenai channel `chanOut` sendiri, akan di-close ketika dipastikan **semua file sudah dikirim datanya ke channel tersebut** (silakan lihat statement `close(chanOut)` di akhir goroutine).
Ok lanjut, karena di sini ada channel yang digunakan sebagai media pengiriman data (`FileInfo`), maka juga harus ada penerima data channel-nya dong. Yups.
-#### • Pipeline 2: MD5 Hash Konten File
+#### ◉ Pipeline 2: MD5 Hash Konten File
Tepat di bawah pipeline 1, tambahkan pemanggilan fungsi `getSum()` sebanyak 3x, bisa lebih banyak sih sebenarnya, bebas. Kemudian jadikan nilai balik pemanggilan fungsi tersebut sebagai variadic argument pemanggilan fungsi `mergeChanFileInfo()`.
@@ -390,7 +390,7 @@ Jadi TL;DR nya:
* Fungsi Fan-out digunakan untuk pembuatan worker, untuk distribusi job, yang proses distribusinya sendiri akan berhenti ketika channel inputan di-close.
* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang di mana channel baru ini juga otomatis di-close ketika channel input adalah closed.
-Lanjut buat fungsi `getSum()`-nya.
+Sekarang lanjut buat fungsi `getSum()`.
```go
func getSum(chanIn <-chan FileInfo) <-chan FileInfo {
@@ -447,7 +447,7 @@ Secara garis besar, pada fungsi ini terjadi beberapa proses:
* Channel `chanOut` ini dijadikan sebagai nilai balik fungsi.
* Di situ kita gunakan `sync.WaitGroup` untuk kontrol goroutine. Kita akan tunggu hingga semua channel input adalah closed, setelah itu barulah kita close channel `chanOut` ini.
-#### • Pipeline 3: Rename file
+#### ◉ Pipeline 3: Rename file
Tambahkan statement pipeline ketiga, yaitu pemanggilan fungsi Fan-out `rename()`, lalu panggil fungsi Fan-in `mergeChanFileInfo()` untuk multiplex channel kembalian fungsi `rename()`.
@@ -493,7 +493,7 @@ Bisa dilihat di atas kita rename file asli yang informasi path-nya ada di `FileI
Setelah semua file berhasil di-rename, maka channel `chanOut` di-close.
-#### • Pipeline 4 / Output
+#### ◉ Pipeline 4 / Output
Serangkaian proses yang sudah kita setup punya ketergantungan tinggi satu sama lain, dan eksekusinya harus berurutan meskipun *concurrently*. Ini secara langsung juga mempermudah kita dalam mengolah output hasil pipeline. Kita cukup fokus ke channel hasil Fan-in yang paling terakhir, yaitu channel `chanRename`.
@@ -528,9 +528,11 @@ Bisa dilihat bedanya, untuk rename 3000 file menggunakan cara sekuensial membutu
## A.62.6. Kesimpulan
-Pipeline concurrency pattern sangat bagus untuk diterapkan pada kasus yang proses-nya bisa di-klasifikasi menjadi sub-proses kecil-kecil yang secara I/O tidak saling tunggu (tapi secara flow harus berurutan).
+Pipeline concurrency pattern sangat bagus untuk diterapkan pada case yang proses-nya bisa diklasifikasi menjadi sub-proses kecil-kecil yang secara I/O tidak saling tunggu (tapi secara flow harus berurutan).
+
+Untuk banyak kasus, metode pipeline ini sangat tepat guna. Kita bisa dengan mudah mengontrol penggunaan resource seperti **CPU** dengan cara menentukan angka ideal jumlah worker untuk masing-masing pipeline, tapi untuk bagian ini butuh *test and try* juga, karena tidak selalu banyak worker itu menghasilkan proses yang lebih cepat, dan misalpun bisa, perlu dicek juga konsumsi resource-nya berlebihan atau tidak. Bisa jadi karena terlalu banyak worker malah lebih lambat karena ada constraint I/O.
-Untuk banyak kasus, metode pipeline ini sangat tepat guna. Kita bisa dengan mudah mengontrol penggunaan resource seperti **CPU** dengan cara menentukan angka ideal jumlah worker untuk masing-masing pipeline, tapi untuk bagian ini butuh *test and try*, karena tidak selalu banyak worker itu menghasilkan proses yang lebih cepat. Bisa jadi karena terlalu banyak worker malah lebih lambat. Jadi silakan lakukan testing saja, sesuaikan dengan spesifikasi CPU laptop/komputer/server yang digunakan.
+Intinya butuh banyak percobaan dan testing, sesuaikan dengan spesifikasi hardware laptop/komputer/server yang digunakan.
Ok sekian untuk chapter panjang ini.
diff --git a/content/A-data-type-conversion.md b/content/A-data-type-conversion.md
index 370ec902f..135d4f3ba 100644
--- a/content/A-data-type-conversion.md
+++ b/content/A-data-type-conversion.md
@@ -1,12 +1,12 @@
# A.43. Konversi Antar Tipe Data
-Pada chapter sebelum-sebelumnyanya kita sudah mengaplikasikan beberapa cara konversi data, contohnya seperti konversi `string` ↔ `int` menggunakan `strconv`, dan `time.Time` ↔ `string`. Pada chapter ini kita akan belajar lebih banyak.
+Di beberapa chapter sebelum ini kita telah menerapkan beberapa cara konversi data, contohnya seperti konversi `string` ↔ `int` menggunakan `strconv`, dan `time.Time` ↔ `string`. Pada chapter ini kita akan mempelajarinya lebih detail.
## A.43.1. Konversi Menggunakan `strconv`
Package `strconv` berisi banyak fungsi yang sangat membantu kita untuk melakukan konversi. Berikut merupakan beberapa fungsi yang dalam package tersebut.
-#### • Fungsi `strconv.Atoi()`
+#### ◉ Fungsi `strconv.Atoi()`
Fungsi ini digunakan untuk konversi data dari tipe `string` ke `int`. `strconv.Atoi()` menghasilkan 2 buah nilai kembalian, yaitu hasil konversi dan `error` (jika konversi sukses, maka `error` berisi `nil`).
@@ -26,7 +26,7 @@ func main() {
}
```
-#### • Fungsi `strconv.Itoa()`
+#### ◉ Fungsi `strconv.Itoa()`
Merupakan kebalikan dari `strconv.Atoi`, berguna untuk konversi `int` ke `string`.
@@ -37,7 +37,7 @@ var str = strconv.Itoa(num)
fmt.Println(str) // "124"
```
-#### • Fungsi `strconv.ParseInt()`
+#### ◉ Fungsi `strconv.ParseInt()`
Digunakan untuk konversi `string` berbentuk numerik dengan basis tertentu ke tipe numerik non-desimal dengan lebar data bisa ditentukan.
@@ -63,7 +63,7 @@ if err == nil {
}
```
-#### • Fungsi `strconv.FormatInt()`
+#### ◉ Fungsi `strconv.FormatInt()`
Berguna untuk konversi data numerik `int64` ke `string` dengan basis numerik bisa ditentukan sendiri.
@@ -74,7 +74,7 @@ var str = strconv.FormatInt(num, 8)
fmt.Println(str) // 30
```
-#### • Fungsi `strconv.ParseFloat()`
+#### ◉ Fungsi `strconv.ParseFloat()`
Digunakan untuk konversi `string` ke numerik desimal dengan lebar data bisa ditentukan.
@@ -89,7 +89,7 @@ if err == nil {
Pada contoh di atas, string `"24.12"` dikonversi ke float dengan lebar tipe data `float32`. Hasil konversi `strconv.ParseFloat` adalah sesuai dengan standar [IEEE Standard for Floating-Point Arithmetic](https://en.wikipedia.org/wiki/IEEE_floating_point).
-#### • Fungsi `strconv.FormatFloat()`
+#### ◉ Fungsi `strconv.FormatFloat()`
Berguna untuk konversi data bertipe `float64` ke `string` dengan format eksponen, lebar digit desimal, dan lebar tipe data bisa ditentukan.
@@ -113,7 +113,7 @@ Ada beberapa format eksponen yang bisa digunakan. Detailnya bisa dilihat di tabe
| `g` | Akan menggunakan format eksponen `e` untuk eksponen besar dan `f` untuk selainnya |
| `G` | Akan menggunakan format eksponen `E` untuk eksponen besar dan `f` untuk selainnya |
-#### • Fungsi `strconv.ParseBool()`
+#### ◉ Fungsi `strconv.ParseBool()`
Digunakan untuk konversi `string` ke `bool`.
@@ -126,7 +126,7 @@ if err == nil {
}
```
-#### • Fungsi `strconv.FormatBool()`
+#### ◉ Fungsi `strconv.FormatBool()`
Digunakan untuk konversi `bool` ke `string`.
@@ -139,12 +139,14 @@ fmt.Println(str) // true
## A.43.2. Konversi Data Menggunakan Teknik Casting
-Keyword tipe data bisa digunakan untuk casting, atau konversi antar tipe data. Cara penggunaannya adalah dengan menuliskan tipe data tujuan casting sebagai fungsi, lalu menyisipkan data yang akan dikonversi sebagai parameter fungsi tersebut.
+Cara penerapannya adalah dengan menggunakan keyword tipe data sebagai nama fungsi, kemudiaan argument pemanggilannya diisi dengan data yang ingin dikonversi tipenya.
```go
+// konversi nilai 24 bertipe int ke float64
var a float64 = float64(24)
fmt.Println(a) // 24
+// konversi nilai 24.00 bertipe float32 ke int32
var b int32 = int32(24.00)
fmt.Println(b) // 24
```
@@ -175,7 +177,7 @@ fmt.Printf("%s \n", s)
// halo
```
-Pada contoh di-atas, beberapa kode byte dituliskan dalam bentuk slice, ditampung variabel `byte1`. Lalu, nilai variabel tersebut di-cast ke `string`, untuk kemudian ditampilkan.
+Di contoh ke-2 di-atas, beberapa kode byte dituliskan dalam bentuk slice, ditampung variabel `byte1`. Lalu, nilai variabel tersebut di-cast ke `string`, untuk kemudian ditampilkan.
Selain itu, tiap karakter string juga bisa di-casting ke bentuk `int`, hasilnya adalah sama yaitu data byte dalam bentuk numerik basis desimal, dengan ketentuan literal string yang digunakan adalah tanda petik satu ('
).
@@ -189,9 +191,9 @@ var d string = string(104)
fmt.Println(d) // h
```
-## A.43.4. Type Assertions Pada Interface Kosong (`interface{}`)
+## A.43.4. Type Assertions Pada Tipe `any` atau Interface Kosong (`interface{}`)
-**Type assertions** merupakan teknik untuk mengambil tipe data konkret dari data yang terbungkus dalam `interface{}`. Jadi bisa disimpulkan bahwa teknik type assertions hanya bisa dilakukan pada data bertipe `interface{}`. Lebih jelasnya silakan cek contoh berikut.
+**Type assertions** merupakan teknik untuk mengambil tipe data konkret dari data yang terbungkus dalam `interface{}` atau `any`. Lebih jelasnya silakan cek contoh berikut.
Variabel `data` disiapkan bertipe `map[string]interface{}`, map tersebut berisikan beberapa item dengan tipe data value-nya berbeda satu sama lain, sementara tipe data untuk key-nya sama yaitu `string`.
@@ -215,7 +217,7 @@ Statement `data["nama"].(string)` maksudnya adalah, nilai `data["nama"]` yang be
Pada kode di atas, tidak akan terjadi panic error, karena semua operasi type assertion adalah dilakukan menggunakan tipe data yang sudah sesuai dengan tipe data nilai aslinya. Seperti `data["nama"]` yang merupakan `string` pasti bisa di-asertasi ke tipe `string`.
-Coba lakukan asertasi ke tipe yang tidak sesuai dengan tipe nilai aslinya, seperti `data["nama"].(int)`, pasti akan men-trigger panic error.
+Coba lakukan asertasi ke tipe yang tidak sesuai dengan tipe nilai aslinya, seperti `data["nama"].(int)`, hasilnya adalah panic error.
Nah, dari penjelasan di atas, terlihat bahwa kita harus tau terlebih dahulu apa tipe data asli dari data yang tersimpan dalam interface. Jika misal tidak tau, maka bisa gunakan teknik di bawah ini untuk pengecekan sukses tidaknya proses asertasi.
diff --git a/content/A-defer-exit.md b/content/A-defer-exit.md
index 0f891f4e9..bb719c471 100644
--- a/content/A-defer-exit.md
+++ b/content/A-defer-exit.md
@@ -4,7 +4,7 @@
## A.36.1. Penerapan keyword `defer`
-Seperti yang sudah dijelaskan secara singkat di atas, bahwa defer digunakan untuk mengakhirkan eksekusi baris kode **dalam skope blok fungsi**. Ketika eksekusi blok sudah hampir selesai, statement yang di-defer dijalankan.
+Seperti yang sudah dijelaskan singkat di atas, bahwa defer digunakan untuk mengakhirkan eksekusi baris kode **dalam skope blok fungsi**. Ketika eksekusi blok sudah hampir selesai, statement yang di-defer dijalankan.
Defer bisa ditempatkan di mana saja, awal maupun akhir blok. Tetapi tidak mempengaruhi kapan waktu dieksekusinya, akan selalu dieksekusi di akhir.
@@ -19,7 +19,7 @@ func main() {
}
```
-Output:
+Output program:
![Penerapan `defer`](images/A_defer_exit_1_defer.png)
@@ -45,7 +45,7 @@ func orderSomeFood(menu string) {
}
```
-Output:
+Output program:
![Penerapan `defer` dengan `return`](images/A_defer_exit_2_defer_return.png)
@@ -68,7 +68,7 @@ func main() {
}
```
-Output:
+Output program:
```
halo 1
@@ -95,7 +95,7 @@ func main() {
}
```
-Output:
+Output program:
```
halo 1
diff --git a/content/A-encoding-base64.md b/content/A-encoding-base64.md
index 08a092cd0..5d4bbfd1d 100644
--- a/content/A-encoding-base64.md
+++ b/content/A-encoding-base64.md
@@ -1,8 +1,8 @@
# A.46. Encode - Decode Base64
-Go memiliki package `encoding/base64`, berisikan fungsi-fungsi untuk kebutuhan **encode** dan **decode** data ke base64 dan sebaliknya. Data yang akan di-encode harus bertipe `[]byte`, perlu dilakukan casting untuk data-data yang belum sesuai tipenya.
+Go menyediakan package `encoding/base64`, berisikan fungsi-fungsi untuk kebutuhan **encode** dan **decode** data ke bentuk base64 dan sebaliknya. Data yang akan di-encode harus bertipe `[]byte`, maka perlu dilakukan casting untuk data-data yang tipenya belum `[]byte`.
-Ada beberapa cara yang bisa digunakan untuk encode dan decode data, dan pada chapter ini kita akan mempelajarinya.
+Proses encoding dan decoding bisa dilakukan via beberapa cara yang pada chapter ini kita akan pelajari.
## A.46.1. Penerapan Fungsi `EncodeToString()` & `DecodeString()`
@@ -59,7 +59,7 @@ Fungsi `base64.StdEncoding.EncodedLen(len(data))` menghasilkan informasi lebar v
Fungsi `base64.StdEncoding.DecodedLen()` memiliki kegunaan sama dengan `EncodedLen()`, hanya saja digunakan untuk keperluan decoding.
-Dibanding 2 fungsi sebelumnya, fungsi `Encode()` dan `Decode()` memiliki beberapa perbedaan. Selain lebar data penampung encode/decode harus dicari terlebih dahulu, terdapat perbedaan lainnya, yaitu pada fungsi ini hasil encode/decode tidak didapat dari nilai kembalian, melainkan dari parameter. Variabel yang digunakan untuk menampung hasil, disisipkan pada parameter fungsi tersebut.
+Dibanding 2 fungsi sebelumnya, fungsi `Encode()` dan `Decode()` ini memiliki beberapa perbedaan. Selain lebar data penampung encode/decode harus dicari terlebih dahulu, terdapat perbedaan lainnya, yaitu pada fungsi ini hasil encode/decode tidak didapat dari nilai kembalian, melainkan dari parameter. Variabel yang digunakan untuk menampung hasil, disisipkan pada parameter fungsi tersebut.
Pada pemanggilan fungsi encode/decode, variabel `encoded` dan `decoded` tidak disisipkan nilai pointer-nya, cukup di-pass dengan cara biasa, tipe datanya sudah dalam bentuk `[]byte`.
diff --git a/content/A-error-panic-recover.md b/content/A-error-panic-recover.md
index 6a2291e4a..dca1c4ff4 100644
--- a/content/A-error-panic-recover.md
+++ b/content/A-error-panic-recover.md
@@ -1,14 +1,18 @@
# A.37. Error, Panic, dan Recover
-Error merupakan topik yang sangat penting dalam pemrograman Go. Di bagian ini kita akan belajar mengenai pemanfaatan error dan cara membuat custom error sendiri. Selain itu, kita juga akan belajar tentang penggunaan **panic** untuk memunculkan panic error, dan **recover** untuk mengatasinya.
+Error merupakan topik yang sangat penting dalam pemrograman Go, salah satu alasannya karena Go tidak mengadopsi konsep exception.
+
+Pada chapter ini kita akan belajar tentang pemanfaatan error dan cara membuat custom error. Selain itu, kita juga akan belajar tentang penggunaan **panic** untuk memunculkan panic error, dan **recover** untuk mengatasinya.
## A.37.1. Pemanfaatan Error
-`error` merupakan sebuah tipe. Error memiliki 1 buah property berupa method `Error()`, method ini mengembalikan detail pesan error dalam string. Error termasuk tipe yang isinya bisa `nil`.
+Di go, `error` merupakan sebuah tipe data. Error memiliki beberapa property yang salah satunya adalah method `Error()`. Method ini mengembalikan detail pesan error dalam string. Error termasuk tipe yang isinya bisa `nil`.
+
+Pada praktik pemrograman Go, pembaca akan menemui banyak sekali fungsi yang mengembalikan nilai balik lebih dari satu, yang biasanya salah satunya adalah bertipe `error`.
-Di Go, banyak sekali fungsi yang mengembalikan nilai balik lebih dari satu. Biasanya, salah satu kembalian adalah bertipe `error`. Contohnya seperti pada fungsi `strconv.Atoi()`. Fungsi tersebut digunakan untuk konversi data string menjadi numerik. Fungsi ini mengembalikan 2 nilai balik. Nilai balik pertama adalah hasil konversi, dan nilai balik kedua adalah `error`. Ketika konversi berjalan mulus, nilai balik kedua akan bernilai `nil`. Sedangkan ketika konversi gagal, penyebabnya bisa langsung diketahui dari error yang dikembalikan.
+Contohnya seperti pada fungsi `strconv.Atoi()`. Fungsi tersebut digunakan untuk konversi data string menjadi numerik. Fungsi ini mengembalikan 2 nilai balik. Nilai balik pertama adalah hasil konversi, dan nilai balik kedua adalah `error`. Ketika konversi berjalan mulus, nilai balik kedua akan bernilai `nil`. Sedangkan ketika konversi gagal, penyebabnya bisa langsung diketahui dari error yang dikembalikan.
-Di bawah ini merupakan contoh program sederhana untuk deteksi inputan dari user, apakah numerik atau bukan. Dari sini kita akan belajar mengenai pemanfaatan error.
+Di bawah ini merupakan contoh program sederhana untuk deteksi inputan dari user, apakah numerik atau bukan. Pada kode tersebut kita akan belajar mengenai pemanfaatan error.
```go
package main
@@ -93,17 +97,21 @@ Fungsi `validate()` mengembalikan 2 data. Data pertama adalah nilai `bool` yang
Fungsi `strings.TrimSpace()` digunakan untuk menghilangkan karakter spasi sebelum dan sesudah string. Ini dibutuhkan karena user bisa saja menginputkan spasi lalu enter.
-Ketika inputan tidak valid, maka error baru dibuat dengan memanfaatkan fungsi `errors.New()`. Selain itu objek error juga bisa dibuat lewat fungsi `fmt.Errorf()`.
+Ketika inputan tidak valid, maka error baru dibuat dengan memanfaatkan fungsi `errors.New()`.
![Custom error](images/A_error_panic_recover_2_custom_error.png)
+Selain menggunakan `errors.New()`, objek error bisa dibuat via fungsi `fmt.Errorf()`. Pengaplikasiannya mirip, perbedaannya fungsi `fmt.Errorf()` mendukung format string.
+
## A.37.3. Penggunaan `panic`
-Panic digunakan untuk menampilkan *stack trace* error sekaligus menghentikan flow goroutine (karena `main()` juga merupakan goroutine, maka behaviour yang sama juga berlaku). Setelah ada panic, proses akan terhenti, apapun setelah tidak di-eksekusi kecuali proses yang sudah di-defer sebelumnya (akan muncul sebelum panic error).
+Panic digunakan untuk menampilkan *stack trace* error sekaligus menghentikan flow goroutine. Setelah ada panic, proses akan terhenti, apapun setelah tidak di-eksekusi kecuali proses yang sudah di-defer sebelumnya (akan muncul sebelum panic error).
+
+> Perlu diingat bahwa `main()` juga merupakan goroutine, maka behaviour yang sama adalah berlaku.
-Panic menampilkan pesan error di console, sama seperti `fmt.Println()`. Informasi error yang ditampilkan adalah stack trace error, jadi sangat mendetail dan heboh.
+Panic menampilkan pesan error di console, sama seperti `fmt.Println()`. Informasi error yang ditampilkan adalah stack trace error, isinya sangat detail dan heboh.
-Kembali ke koding, pada program yang telah kita buat tadi, ubah `fmt.Println()` yang berada di dalam blok kondisi `else` pada fungsi main menjadi `panic()`, lalu tambahkan `fmt.Println()` setelahnya.
+Kembali ke praktek, pada program yang telah kita buat tadi, ubah `fmt.Println()` yang berada di dalam blok kondisi `else` pada fungsi main menjadi `panic()`, lalu tambahkan `fmt.Println()` setelahnya.
```go
func main() {
@@ -126,7 +134,7 @@ Jalankan program lalu langsung tekan enter, maka panic error muncul dan baris ko
## A.37.4. Penggunaan `recover`
-Recover berguna untuk meng-handle panic error. Pada saat panic error muncul, recover men-take-over goroutine yang sedang panic (pesan panic tidak akan muncul).
+Recover berguna untuk meng-handle panic error. Pada saat panic error muncul, recover men-take-over goroutine yang sedang panic dan efek sampingnya pesan panic tidak muncul dan eksekusi program adalah tidak error.
Ok, mari kita modifikasi sedikit fungsi di-atas untuk mempraktekkan bagaimana cara penggunaan recover. Tambahkan fungsi `catch()`, dalam fungsi ini terdapat statement `recover()` yang dia akan mengembalikan pesan panic error yang seharusnya muncul.
@@ -157,7 +165,7 @@ func main() {
}
```
-Output:
+Output program:
![Handle panic menggunakan recover](images/A_error_panic_recover_4_recover.png)
@@ -207,7 +215,7 @@ func main() {
}
```
-Pada kode di atas, bisa dilihat di dalam perulangan terdapat sebuah IIFE untuk recover panic dan juga ada kode untuk men-trigger panic error secara paksa. Ketika panic error terjadi, maka idealnya perulangan terhenti, tetapi pada contoh di atas tidak, dikarenakan operasi dalam perulangan sudah di bungkus dalam IIFE dan seperti yang kita tau sifat panic error adalah menghentikan proses secara paksa dalam scope blok fungsi.
+Bisa dilihat di dalam perulangan terdapat sebuah IIFE untuk recover panic dan juga ada kode untuk men-trigger panic error secara paksa. Ketika panic error terjadi, maka idealnya perulangan terhenti, tetapi pada contoh di atas tidak, dikarenakan operasi dalam perulangan sudah di bungkus dalam IIFE dan seperti yang kita tau sifat panic error adalah menghentikan proses secara paksa dalam scope blok fungsi.
---
diff --git a/content/A-exec.md b/content/A-exec.md
index d472f8544..7c05f887e 100644
--- a/content/A-exec.md
+++ b/content/A-exec.md
@@ -1,12 +1,12 @@
# A.49. Exec
-**Exec** digunakan untuk eksekusi perintah command line lewat kode program. Command yang bisa dieksekusi adalah semua command yang bisa dieksekusi di terminal (atau CMD untuk pengguna Windows).
+**Exec** digunakan untuk eksekusi perintah command line lewat kode program. Command yang bisa dieksekusi adalah semua command yang bisa dieksekusi di command line sesuai sistem operasinya (Linux-distros, Windows, MacOS, dan lainnya).
## A.49.1. Penggunaan Exec
-Go menyediakan package `exec` berisikan banyak fungsi untuk keperluan eksekusi perintah CLI.
+Go menyediakan package `exec` isinya banyak sekali API atau fungsi untuk keperluan eksekusi perintah command line.
-Cara untuk eksekusi command cukup mudah, yaitu dengan menuliskan command dalam bentuk string, diikuti arguments-nya (jika ada) sebagai parameter variadic pada fungsi `exec.Command()`.
+Cara eksekusi command adalah menggunakan fungsi `exec.Command()` dengan argument pemanggilan fungsi diisi command CLI yang diinginkan. Contoh:
```go
package main
@@ -26,15 +26,17 @@ func main() {
}
```
-Fungsi `exec.Command()` digunakan untuk menjalankan command. Fungsi tersebut bisa langsung di-chain dengan method `Output()`, jika ingin mendapatkan outputnya. Output yang dihasilkan berbentuk `[]byte`, gunakan cast ke string untuk mengambil bentuk string-nya.
+Fungsi `exec.Command()` menjalankan command yang dituliskan pada argument pemanggilan fungsi.
+
+Untuk mendapatkan outputnya, chain saja langsung dengan method `Output()`. Output yang dihasilkan berbentuk `[]byte`, maka pastikan cast ke string terlebih dahulu untuk membaca isi outputnya.
![Ekeskusi command menggunakan exec](images/A_exec_1_exec.png)
## A.49.2. Rekomendasi Penggunaan Exec
-Kadang kala, pada saat eksekusi command yang sudah jelas-jelas ada (seperti `ls`, `dir`, atau lainnya) kita menemui error yang mengatakan command not found. Hal itu terjadi karena executable dari command-command tersebut tidak ada. Seperti di windows tidak ada `dir.exe` dan lainnya. Di OS non-windows-pun juga demikian.
+Ada kalanya saat eksekusi command yang sudah jelas-jelas ada (seperti `ls`, `dir`, atau lainnya), error muncul menginformasikan bahwa command tidak ditemukan (command not found). Hal ini biasanya terjadi karena executable dari command-command tersebut tidak ada. Seperti di windows tidak ada `cmd` atau `cmd.exe`, di Linux tidak ditentukan apakah memakai `bash` atau `shell`, dan lainnya
-Untuk mengatasi masalah ini, tambahkan `bash -c` pada linux/nix command atau `cmd /C` untuk windows.
+Untuk mengatasi masalah ini, tambahkan `bash -c` pada sistem operasi berbasi Linux, MacOS, Unix, atau `cmd /C` untuk OS Windows.
```go
if runtime.GOOS == "windows" {
@@ -44,11 +46,11 @@ if runtime.GOOS == "windows" {
}
```
-Statement `runtime.GOOS` mengembalikan informasi sistem operasi dalam string.
+Statement `runtime.GOOS` penggunaannya mengembalikan informasi sistem operasi dalam bentuk string. Manfaatkan seleksi kondisi untuk memastikan command yang ingin dieksekusi sudah sesuai dengan OS atau belum.
## A.49.3. Method Exec Lainnya
-Selain `.Output()` ada sangat banyak sekali API untuk keperluan komunikasi dengan OS/CLI yang bisa dipergunakan. Detailnya silakan langsung merujuk ke dokumentasi [https://golang.org/pkg/os/exec/](https://golang.org/pkg/os/exec/)
+Selain `.Output()` ada sangat banyak sekali API untuk keperluan komunikasi dengan OS/CLI yang bisa dipergunakan. Lebih detailnya silakan langsung melihat dokumentasi package tersebut di [https://golang.org/pkg/os/exec/](https://golang.org/pkg/os/exec/)
---
diff --git a/content/A-file.md b/content/A-file.md
index 2ab152a7b..58b386141 100644
--- a/content/A-file.md
+++ b/content/A-file.md
@@ -1,12 +1,12 @@
# A.50. File
-Ada beberapa cara yang bisa digunakan untuk operasi file di Go. Pada chapter ini kita akan mempelajari teknik yang paling dasar, yaitu dengan memanfaatkan `os.File`.
+Pada chapter ini kita akan belajar beberapa teknik operasi file yang paling dasar.
## A.50.1. Membuat File Baru
-Pembuatan file di Go sangatlah mudah, cukup dengan memanggil fungsi `os.Create()` lalu memasukkan path file yang ingin dibuat sebagai parameter. Jika ternyata file yang akan dibuat sudah ada, maka akan ditimpa. Bisa memanfaatkan `os.IsNotExist()` untuk mendeteksi apakah file sudah dibuat atau belum.
+Pembuatan file di Go sangat mudah, dilakukan dengan memanfaatkan fungsi `os.Create()` disertai path file sebagai argument pemanggilan fungsi. Jika ternyata file yang akan dibuat sudah ada duluan, maka operasi `os.Create()` akan menimpa file yang sudah ada dengan file baru. Untuk menghindari penimpaan file, gunakan fungsi `os.IsNotExist()` untuk mendeteksi apakah file yang ingin dibuat sudah ada atau belum.
-Berikut merupakan contoh pembuatan file.
+Contoh program operasi pembuatan file:
```go
package main
@@ -43,17 +43,17 @@ func main() {
}
```
-Fungsi `os.Stat()` mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada). Masukkan error kembalian fungsi tersebut sebagai parameter fungsi `os.IsNotExist()`, untuk mendeteksi apakah file yang akan dibuat sudah ada. Jika belum ada, maka fungsi tersebut akan mengembalikan nilai `true`.
+Fungsi `os.Stat()` mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada). Masukkan error kembalian fungsi tersebut sebagai argument pemanggilan fungsi `os.IsNotExist()`, untuk mengetahui apakah file yang akan dibuat sudah ada. Jika rupanya file belum ada ada, maka fungsi tersebut akan mengembalikan nilai `true`.
-Fungsi `os.Create()` digunakan untuk membuat file pada path tertentu. Fungsi ini mengembalikan objek `*os.File` dari file yang bersangkutan. File yang baru terbuat statusnya adalah otomatis **open**, maka dari itu perlu untuk di-**close** menggunakan method `file.Close()` setelah file tidak digunakan lagi.
+Fungsi `os.Create()` ini mengembalikan objek bertipe `*os.File`. File yang baru dibuat, statusnya adalah otomatis **open**. Setelah operasi file selesai, file harus di-**close** menggunakan method `file.Close()`.
-Membiarkan file terbuka ketika sudah tak lagi digunakan bukan hal yang baik, karena efeknya ke memory dan akses ke file itu sendiri, file akan di-lock sehingga tidak bisa digunakan oleh proses lain selama status file masih open atau belum di-close.
+Membiarkan file terbuka ketika sudah tak lagi digunakan adalah tidak baik, karena ada efek ke memory dan akses ke file itu sendiri, file menjadi terkunci/locked, membuatnya tidak bisa diakses oleh proses lain selama status file statusnya masih **open** dan belum di-close.
![Membuat file baru](images/A_file_1_create.png)
## A.50.2. Mengedit Isi File
-Untuk mengedit file, yang perlu dilakukan pertama adalah membuka file dengan level akses **write**. Setelah mendapatkan objek file-nya, gunakan method `WriteString()` untuk pengisian data. Terakhir panggil method `Sync()` untuk menyimpan perubahan.
+Untuk mengedit file, yang pertama perlu dilakukan adalah membuka file dengan level akses **write**. Setelah mendapatkan objek file-nya, gunakan method `WriteString()` untuk penulisan data. Di akhir, panggil method `Sync()` untuk menyimpan perubahan.
```go
func writeFile() {
@@ -80,13 +80,13 @@ func main() {
}
```
-Pada program di atas, file dibuka dengan level akses **read** dan **write** dengan kode permission **0664**. Setelah itu, beberapa string diisikan ke dalam file tersebut menggunakan `WriteString()`. Di akhir, semua perubahan terhadap file akan disimpan dengan dipanggilnya `Sync()`.
+Pada program di atas, file dibuka dengan level akses **read** dan **write** dengan kode permission **0664**. Setelah itu, beberapa string diisikan ke dalam file tersebut menggunakan `WriteString()`. Di akhir, semua perubahan terhadap file menjadi tersimpan dengan adanya pemanggilan method `Sync()`.
![Mengedit file](images/A_file_2_write.png)
## A.50.3. Membaca Isi File
-File yang ingin dibaca harus dibuka terlebih dahulu menggunakan fungsi `os.OpenFile()` dengan level akses minimal adalah **read**. Setelah itu, gunakan method `Read()` dengan parameter adalah variabel, yang di mana hasil proses baca akan disimpan ke variabel tersebut.
+File yang ingin dibaca harus dibuka terlebih dahulu menggunakan fungsi `os.OpenFile()` dengan level akses minimal adalah **read**. Dari object file kembalian fungsi tersebut, gunakan method `Read()` dengan disertai argument berupa variabel yang akan menampung data hasil operasi baca.
```go
// tambahkan di bagian import package io
@@ -120,21 +120,21 @@ func main() {
}
```
-Pada kode di atas `os.OpenFile()` digunakan untuk membuka file. Fungsi tersebut memiliki beberapa parameter.
+Fungsi `os.OpenFile()` dalam pemanggilannya memerlukan beberapa argument parameter untuk di-isi:
1. Parameter pertama adalah path file yang akan dibuka.
2. Parameter kedua adalah level akses. `os.O_RDONLY` maksudnya adalah **read only**.
3. Parameter ketiga adalah permission file-nya.
-Variabel `text` disiapkan bertipe slice `[]byte` dengan alokasi elemen 1024. Variabel tersebut bertugas menampung data hasil statement `file.Read()`. Proses pembacaan file akan dilakukan terus menerus, berurutan dari baris pertama hingga akhir.
+Variabel `text` disiapkan bertipe slice `[]byte` dengan alokasi elemen `1024`. Variabel tersebut bertugas menampung data hasil statement `file.Read()`. Proses pembacaan file dilakukan terus menerus, berurutan dari baris pertama hingga akhir.
-Error yang muncul ketika eksekusi `file.Read()` akan di-filter, ketika error tersebut adalah selain `io.EOF` maka proses baca file akan berlanjut. Error `io.EOF` sendiri menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau **end of file**.
+Error yang muncul ketika eksekusi `file.Read()` akan di-filter, ketika error adalah selain `io.EOF` maka proses baca file akan berlanjut. Error `io.EOF` sendiri menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau **end of file**.
![Membaca isi file](images/A_file_3_read.png)
## A.50.4. Menghapus File
-Cara menghapus file sangatlah mudah, cukup panggil fungsi `os.Remove()`, masukan path file yang ingin dihapus sebagai parameter.
+Operasi menghapus file dilakukan via fungsi `os.Remove()`. Panggil fungsi tersebut, kemudian isi path dari file yang ingin dihapus sebagai argument fungsi.
```go
func deleteFile() {
diff --git a/content/A-fungsi-closure.md b/content/A-fungsi-closure.md
index 4c60405ec..b8e6ab54b 100644
--- a/content/A-fungsi-closure.md
+++ b/content/A-fungsi-closure.md
@@ -1,12 +1,12 @@
# A.21. Fungsi Closure
-Definisi **Closure** adalah sebuah fungsi yang bisa disimpan dalam variabel. Dengan menerapkan konsep tersebut, kita bisa membuat fungsi di dalam fungsi, atau bahkan membuat fungsi yang mengembalikan fungsi.
-
-Closure merupakan *anonymous function* atau fungsi tanpa nama. Biasa dimanfaatkan untuk membungkus suatu proses yang hanya dipakai sekali atau dipakai pada blok tertentu saja.
+Definisi **Closure** adalah suatu *anonymous function* (atau fungsi tanpa nama) yang disimpan dalam variabel. Dengan adanya closure, kita bisa mendesain beberapa hal diantaranya seperti: membuat fungsi di dalam fungsi, atau bahkan membuat fungsi yang mengembalikan fungsi. Closure biasa dimanfaatkan untuk membungkus suatu proses yang hanya dijalankan sekali saja atau hanya dipakai pada blok tertentu saja.
## A.21.1. Closure Disimpan Sebagai Variabel
-Sebuah fungsi tanpa nama bisa disimpan dalam variabel. Variabel yang menyimpan closure memiliki sifat seperti fungsi yang disimpannya. Di bawah ini adalah contoh program sederhana untuk mencari nilai terendah dan tertinggi dari suatu array. Logika pencarian dibungkus dalam closure yang ditampung oleh variabel `getMinMax`.
+Sebuah fungsi tanpa nama bisa disimpan dalam variabel. Variabel closure memiliki sifat seperti fungsi yang disimpannya.
+
+Di bawah ini adalah contoh program sederhana yang menerapkan closure untuk pencarian nilai terendah dan tertinggi dari data array. Logika pencarian dibungkus dalam closure yang ditampung oleh variabel `getMinMax`.
```go
package main
@@ -35,8 +35,7 @@ func main() {
}
```
-
-Bisa dilihat pada kode di atas bagaimana sebuah closure dibuat dan dipanggil. Sedikit berbeda memang dibanding pembuatan fungsi biasa. Fungsi ditulis tanpa nama, lalu ditampung dalam variabel.
+Bisa dilihat pada kode di atas bagaimana cara deklarasi closure dan cara pemanggilannya. Sedikit berbeda memang dibanding pembuatan fungsi biasa, pada closure fungsi ditulis tanpa memiliki nama lalu ditampung ke variabel.
```go
var getMinMax = func(n []int) (int, int) {
@@ -44,7 +43,7 @@ var getMinMax = func(n []int) (int, int) {
}
```
-Cara pemanggilannya, dengan menuliskan nama variabel tersebut sebagai fungsi, seperti pemanggilan fungsi biasa.
+Cara pemanggilan closure adalah dengan memperlakukan variabel closure seperti fungsi, dituliskan seperti pemanggilan fungsi.
```go
var min, max = getMinMax(numbers)
@@ -54,25 +53,25 @@ Output program:
![Penerapan closure](images/A_fungsi_closure_1_closure.png)
----
+## A.21.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai kode di atas
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.21.1.1. Penggunaan Template String `%v`
+#### ◉ Penggunaan Template String `%v`
-Template `%v` digunakan untuk menampilkan segala jenis data. Bisa array, int, float, bool, dan lainnya.
+Template `%v` digunakan untuk menampilkan data tanpa melihat tipe datanya. Jadi bisa digunakan untuk menampilkan data array, int, float, bool, dan lainnya. Bisa dilihat di contoh statement, data bertipe array dan numerik ditampilkan menggunakan `%v`.
```go
fmt.Printf("data : %v\nmin : %v\nmax : %v\n", numbers, min, max)
```
-Bisa dilihat pada statement di atas, data bertipe array dan numerik ditampilkan menggunakan `%v`. Template ini biasa dimanfaatkan untuk menampilkan sebuah data yang tipe nya bisa dinamis atau belum diketahui. Sangat tepat jika digunakan pada data bertipe `interface{}` yang nantinya akan di bahas pada chapter [A.27. Interface](/A-interface.html).
+Template `%v` ini biasa dimanfaatkan untuk menampilkan sebuah data yang tipe nya bisa dinamis atau belum diketahui. Biasa digunakan untuk keperluan debugging, misalnya untuk menampilkan data bertipe `any` atau `interface{}`.
----
+> Pembahasan mengenai tipe data `any` atau `interface{}` ada di chapter [A.27. Interface](/A-interface.html)
-## A.21.2. Immediately-Invoked Function Expression (IIFE)
+#### ◉ Immediately-Invoked Function Expression (IIFE)
-Closure jenis ini dieksekusi langsung pada saat deklarasinya. Biasa digunakan untuk membungkus proses yang hanya dilakukan sekali, bisa mengembalikan nilai, bisa juga tidak.
+Closure jenis IIFE ini eksekusinya adalah langsung saat deklarasi. Teknik ini biasa diterapkan untuk membungkus proses yang hanya dilakukan sekali. IIFE bisa memiliki nilai balik atau bisa juga tidak.
Di bawah ini merupakan contoh sederhana penerapan metode IIFE untuk filtering data array.
@@ -104,7 +103,7 @@ Output program:
![Penerapan IIFE](images/A_fungsi_closure_2_iife.png)
-Ciri khas IIFE adalah adanya kurung parameter tepat setelah deklarasi closure berakhir. Jika ada parameter, bisa juga dituliskan dalam kurung parameternya.
+Ciri khas dari penulisan IIFE adalah adanya tanda kurung parameter yang ditulis di akhir deklarasi closure. Jika IIFE memiliki parameter, maka argument-nya juga ditulis. Contoh:
```go
var newNumbers = func(min int) []int {
@@ -112,13 +111,13 @@ var newNumbers = func(min int) []int {
}(3)
```
-Pada contoh di atas IIFE menghasilkan nilai balik yang kemudian ditampung `newNumber`. Perlu diperhatikan bahwa yang ditampung adalah **nilai kembaliannya** bukan body fungsi atau **closure**.
+Di contoh sederhana di atas, IIFE menghasilkan nilai balik yang ditampung variabel `newNumber`. Perlu diperhatikan bahwa yang ditampung adalah **nilai kembaliannya** bukan body fungsi atau **closure**-nya.
> Closure bisa juga dengan gaya manifest typing, caranya dengan menuliskan skema closure-nya sebagai tipe data. Contoh:
var closure (func (string, int, []string) int)
closure = func (a string, b int, c []string) int {
// ..
}
## A.21.3. Closure Sebagai Nilai Kembalian
-Salah satu keunikan closure lainnya adalah bisa dijadikan sebagai nilai balik fungsi, cukup aneh memang, tapi pada suatu kondisi teknik ini sangat membantu. Di bawah ini disediakan sebuah fungsi bernama `findMax()`, fungsi ini salah satu nilai kembaliannya berupa closure.
+Salah satu keunikan lain dari closure adalah: closure bisa dijadikan sebagai nilai balik fungsi. Cukup aneh, tapi pada kondisi tertentu teknik ini sangat berguna. Sebagai contoh, di bawah ini dideklarasikan sebuah fungsi bernama `findMax()` yang salah satu nilai kembaliannya adalah berupa closure.
```go
package main
@@ -148,7 +147,12 @@ return len(res), func() []int {
> Fungsi tanpa nama yang akan dikembalikan boleh disimpan pada variabel terlebih dahulu. Contohnya:
var getNumbers = func() []int {
return res
}
return len(res), getNumbers
-Sedikit tentang fungsi `findMax()`, fungsi ini digunakan untuk mencari banyaknya angka-angka yang nilainya di bawah atau sama dengan angka tertentu. Nilai kembalian pertama adalah jumlah angkanya. Nilai kembalian kedua berupa closure yang mengembalikan angka-angka yang dicari. Berikut merupakan contoh implementasi fungsi tersebut.
+Tentang fungsi `findMax()` sendiri, fungsi ini dibuat untuk mempermudah pencarian angka-angka yang nilainya di bawah atau sama dengan angka tertentu. Fungsi ini mengembalikan dua buah nilai balik:
+
+- Nilai balik pertama adalah jumlah angkanya.
+- Nilai balik kedua berupa closure yang mengembalikan angka-angka yang dicari.
+
+Berikut merupakan contoh implementasi fungsi tersebut:
```go
func main() {
diff --git a/content/A-fungsi-multiple-return.md b/content/A-fungsi-multiple-return.md
index 796983568..2ac5893f0 100644
--- a/content/A-fungsi-multiple-return.md
+++ b/content/A-fungsi-multiple-return.md
@@ -1,12 +1,10 @@
# A.19. Fungsi Multiple Return
-Umumnya fungsi hanya memiliki satu buah nilai balik saja. Jika ada kebutuhan di mana data yang dikembalikan harus banyak, biasanya digunakanlah tipe seperti `map`, slice, atau `struct` sebagai nilai balik.
+Di Go, suatu fungsi bisa saja mengembalikan nilai belik lebih dari 1 buah. Teknik ini bisa menjadi alternatif selain menggunakan tipe data kolektif seperti `map`, slice, atau `struct` sebagai nilai balik. Pada chapter ini kita akan belajar penerapannya.
-Go menyediakan kapabilitas bagi programmer untuk membuat fungsi memiliki banyak nilai balik. Pada chapter ini akan dibahas bagaimana penerapannya.
+## A.19.1. Penerapan Fungsi Multiple Return
-## A.19.1 Penerapan Fungsi Multiple Return
-
-Cara membuat fungsi yang memiliki banyak nilai balik tidaklah sulit. Tinggal tulis saja pada saat deklarasi fungsi semua tipe data nilai yang dikembalikan, dan pada keyword `return` tulis semua data yang ingin dikembalikan. Contoh bisa dilihat pada berikut.
+Cara membuat fungsi agar memiliki banyak nilai balik tidaklah sulit, caranya pada saat deklarasi fungsi, tulis semua tipe data nilai balik yang ingin dikembalikan. Kemudian dalam body fungsi, pada penggunaan keyword `return`, tulis semua data yang ingin dikembalikan. Contoh:
```go
package main
@@ -25,7 +23,7 @@ func calculate(d float64) (float64, float64) {
}
```
-Fungsi `calculate()` di atas menerima satu buah parameter (`diameter`) yang digunakan dalam proses perhitungan. Di dalam fungsi tersebut ada 2 hal yang dihitung, yaitu nilai **luas** dan **keliling**. Kedua nilai tersebut kemudian dijadikan sebagai return value fungsi.
+Fungsi `calculate()` di atas memiliki satu buah parameter yaitu `d` (diameter). Di dalam fungsi terdapat operasi perhitungan nilai **luas** dan **keliling** dari nilai `d`. Kedua hasilnya kemudian dijadikan sebagai return value.
Cara pendefinisian banyak nilai balik bisa dilihat pada kode di atas, langsung tulis tipe data semua nilai balik dipisah tanda koma, lalu ditambahkan kurung di antaranya.
@@ -39,7 +37,7 @@ Tak lupa di bagian penulisan keyword `return` harus dituliskan juga semua data y
return area, circumference
```
-Implementasi dari fungsi `calculate()` di atas, bisa dilihat pada kode berikut.
+Sekarang, coba panggil fungsi `calculate()` yang sudah dibuat untuk mencari nilai luas dan keliling dari suatu diameter.
```go
func main() {
@@ -55,13 +53,13 @@ Output program:
![Penerapan teknik multiple return](images/A_fungsi_multiple_return_1_multiple_return.png)
-Karena fungsi tersebut memiliki banyak nilai balik, maka pada pemanggilannya harus disiapkan juga banyak variabel untuk menampung nilai kembalian yang ada (sesuai jumlah nilai balik fungsi).
+Fungsi `calculate()` memiliki banyak nilai balik, maka dalam pemanggilannya harus disiapkan juga sejumlah variabel untuk menampung nilai balik fungsi (sesuai dengan jumlah nilai balik yang dideklarasikan).
```go
var area, circumference = calculate(diameter)
```
-## A.19.2 Fungsi Dengan Predefined Return Value
+## A.19.2. Fungsi Dengan Predefined Return Value
Keunikan lainnya yang jarang ditemui di bahasa lain adalah, di Go variabel yang digunakan sebagai nilai balik bisa didefinisikan di awal.
@@ -84,15 +82,15 @@ Fungsi dideklarasikan memiliki 2 buah tipe data, dan variabel yang nantinya dija
Karena variabel nilai balik sudah ditentukan di awal, untuk mengembalikan nilai cukup dengan memanggil `return` tanpa perlu diikuti variabel apapun. Nilai terakhir `area` dan `circumference` sebelum pemanggilan keyword `return` adalah hasil dari fungsi di atas.
----
+## A.19.3. Penjelasan tambahan
-Ada beberapa hal baru dari kode di atas yang perlu dibahas, seperti `math.Pow()` dan `math.Pi`. Berikut adalah penjelasannya.
+Ada beberapa hal baru dari kode di atas yang perlu dibahas, diantaranya `math.Pow()` dan `math.Pi`.
-#### • Penggunaan Fungsi `math.Pow()`
+#### ◉ Penggunaan Fungsi `math.Pow()`
-Fungsi `math.Pow()` digunakan untuk memangkat nilai. `math.Pow(2, 3)` berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package `math`.
+Fungsi `math.Pow()` digunakan untuk operasi pangkat nilai. `math.Pow(2, 3)` berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package `math`.
-#### • Penggunaan Konstanta `math.Pi`
+#### ◉ Penggunaan Konstanta `math.Pi`
`math.Pi` adalah konstanta bawaan `package math` yang merepresentasikan **Pi** atau **22/7**.
diff --git a/content/A-fungsi-sebagai-parameter.md b/content/A-fungsi-sebagai-parameter.md
index 8eacd0a12..0e27fde4d 100644
--- a/content/A-fungsi-sebagai-parameter.md
+++ b/content/A-fungsi-sebagai-parameter.md
@@ -1,8 +1,8 @@
# A.22. Fungsi Sebagai parameter
-Setelah pada chapter sebelumnya kita belajar mengenai fungsi yang mengembalikan nilai balik berupa fungsi, kali ini topiknya tidak kalah unik, yaitu fungsi yang digunakan sebagai parameter.
+Pada chapter sebelumnya kita telah belajar tentang fungsi yang mengembalikan nilai balik berupa fungsi. Kali ini topiknya tidak kalah unik, yaitu tentang fungsi yang memiliki parameter sebuah fungsi.
-Di Go, fungsi bisa dijadikan sebagai tipe data variabel. Dari situ sangat memungkinkan untuk menjadikannya sebagai parameter juga.
+Di Go, fungsi bisa dijadikan sebagai tipe data variabel, maka sangat memungkinkan untuk menjadikannya sebagai parameter.
## A.22.1. Penerapan Fungsi Sebagai Parameter
@@ -50,7 +50,7 @@ func main() {
}
```
-Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di atas. Berikut merupakan penjelasannya.
+Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di atas. Berikut adalah penjelasannya:
1. Data array (yang didapat dari parameter pertama) akan di-looping.
2. Di tiap perulangannya, closure `callback` dipanggil, dengan disisipkan data tiap elemen perulangan sebagai parameter.
@@ -62,15 +62,17 @@ Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di
Pada `dataContainsO`, parameter kedua fungsi `filter()` berisikan statement untuk deteksi apakah terdapat substring `"o"` di dalam nilai variabel `each` (yang merupakan data tiap elemen), jika iya, maka kondisi filter bernilai `true`, dan sebaliknya.
-pada contoh ke-2 (`dataLength5`), closure `callback` berisikan statement untuk deteksi jumlah karakter tiap elemen. Jika ada elemen yang jumlah karakternya adalah 5, berarti elemen tersebut lolos filter.
+Pada contoh ke-2 (`dataLength5`), closure `callback` berisikan statement untuk deteksi jumlah karakter tiap elemen. Jika ada elemen yang jumlah karakternya adalah 5, berarti elemen tersebut lolos filter.
-Memang butuh usaha ekstra untuk memahami pemanfaatan closure sebagai parameter fungsi. Tapi setelah paham, penerapan teknik ini pada kondisi yang tepat akan sangat membantu proses pembuatan aplikasi.
+Memang butuh usaha ekstra untuk memahami pemanfaatan closure sebagai parameter fungsi. Tapi setelah paham, penerapan teknik ini pada kondisi yang tepat akan sangat berguna.
## A.22.2. Alias Skema Closure
-Kita sudah mempelajari bahwa closure bisa dimanfaatkan sebagai tipe parameter, contohnya seperti pada fungsi `filter()`. Pada fungsi tersebut kebetulan skema tipe parameter closure-nya tidak terlalu panjang, hanya ada satu buah parameter dan satu buah nilai balik.
+Kita sudah mempelajari bahwa closure bisa dimanfaatkan sebagai tipe parameter, contohnya seperti pada fungsi `filter()`. Di fungsi tersebut kebetulan skema tipe parameter closure-nya tidak terlalu panjang, hanya ada satu buah parameter dan satu buah nilai balik.
-Pada fungsi yang skema-nya cukup panjang, akan lebih baik jika menggunakan alias, apalagi ketika ada parameter fungsi lain yang juga menggunakan skema yang sama. Membuat alias fungsi berarti menjadikan skema fungsi tersebut menjadi tipe data baru. Caranya dengan menggunakan keyword `type`. Contoh:
+Untuk fungsi yang skema-nya cukup panjang, akan lebih baik jika menggunakan alias dalam pendefinisiannya, apalagi ketika ada parameter fungsi lain yang juga menggunakan skema yang sama, maka kita tidak perlu menuliskan skema panjang fungsi tersebut berulang-ulang.
+
+Membuat alias fungsi berarti menjadikan skema fungsi tersebut menjadi tipe data baru. Caranya dengan menggunakan keyword `type`. Contoh:
```go
type FilterCallback func(string) bool
@@ -82,11 +84,11 @@ func filter(data []string, callback FilterCallback) []string {
Skema `func(string) bool` diubah menjadi tipe dengan nama `FilterCallback`. Tipe tersebut kemudian digunakan sebagai tipe data parameter `callback`.
----
+## A.22.3. Penjelasan tambahan
Di bawah ini merupakan penjelasan tambahan mengenai fungsi `strings.Contains()`.
-## A.22.2.1. Penggunaan Fungsi `string.Contains()`
+#### ◉ Penggunaan Fungsi `string.Contains()`
Inti dari fungsi ini adalah untuk deteksi apakah sebuah substring adalah bagian dari string, jika iya maka akan bernilai `true`, dan sebaliknya. Contoh penggunaannya:
diff --git a/content/A-fungsi-variadic.md b/content/A-fungsi-variadic.md
index 75e54f00a..7263e9926 100644
--- a/content/A-fungsi-variadic.md
+++ b/content/A-fungsi-variadic.md
@@ -1,16 +1,16 @@
# A.20. Fungsi Variadic
-Go mengadopsi konsep **variadic function** atau pembuatan fungsi dengan parameter sejenis yang tak terbatas. Maksud **tak terbatas** di sini adalah jumlah parameter yang disisipkan ketika pemanggilan fungsi bisa berapa saja.
+Go mengadopsi konsep **variadic function** atau pembuatan fungsi dengan parameter bisa menampung nilai sejenis yang tidak terbatas jumlahnya.
-Parameter variadic memiliki sifat yang mirip dengan slice. Nilai dari parameter-parameter yang disisipkan bertipe data sama, dan ditampung oleh sebuah variabel saja. Cara pengaksesan tiap datanya juga sama, dengan menggunakan index.
+Parameter variadic memiliki sifat yang mirip dengan slice, yaitu nilai dari parameter-parameter yang disisipkan bertipe data sama, dan kesemuanya cukup ditampung oleh satu variabel saja. Cara pengaksesan tiap nilai juga mirip, yaitu dengan menggunakan index.
Pada chapter ini kita akan belajar mengenai cara penerapan fungsi variadic.
## A.20.1. Penerapan Fungsi Variadic
-Deklarasi parameter variadic sama dengan cara deklarasi variabel biasa, pembedanya adalah pada parameter jenis ini ditambahkan tanda titik tiga kali (`...`) tepat setelah penulisan variabel (sebelum tipe data). Nantinya semua nilai yang disisipkan sebagai parameter akan ditampung oleh variabel tersebut.
+Deklarasi parameter variadic sama dengan cara deklarasi variabel biasa, pembedanya adalah pada parameter jenis ini ditambahkan tanda titik tiga kali (`...`) tepat setelah penulisan variabel, sebelum tipe data. Nantinya semua nilai yang disisipkan sebagai parameter akan ditampung oleh variabel tersebut.
-Berikut merupakan contoh penerepannya.
+Contoh program:
```go
package main
@@ -56,19 +56,21 @@ Nilai tiap parameter bisa diakses seperti cara pengaksesan tiap elemen slice. Pa
for _, number := range numbers {
```
----
+## A.20.2. Penjelasan tambahan
-Berikut merupakan penjelasan tambahan dari kode yang telah kita tulis.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Penggunaan Fungsi `fmt.Sprintf()`
+#### ◉ Penggunaan Fungsi `fmt.Sprintf()`
-Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada kasus di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
+Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada case di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
Selain `fmt.Sprintf()`, ada juga `fmt.Sprint()` dan `fmt.Sprintln()`.
-#### • Penggunaan Fungsi `float64()`
+#### ◉ Penggunaan Fungsi `float64()`
+
+Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) menandakan bahwa digunakan untuk keperluan **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-casting.
-Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) berguna untuk **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-cast. Dan cara penerapannya juga sama, cukup panggil sebagai fungsi, lalu masukan data yang ingin dikonversi sebagai parameter.
+Cara penerapan casting: panggil saja tipe data yang diingunkan seperti pemanggilan fungsi, lalu masukan data yang ingin dikonversi sebagai argument pemanggilan fungsi tersebut.
Pada contoh di atas, variabel `total` yang tipenya adalah `int`, dikonversi menjadi `float64`, begitu juga `len(numbers)` yang menghasilkan `int` dikonversi ke `float64`.
@@ -76,11 +78,9 @@ Variabel `avg` perlu dijadikan `float64` karena penghitungan rata-rata lebih ser
Operasi bilangan (perkalian, pembagian, dan lainnya) di Go hanya bisa dilakukan jika tipe datanya sejenis. Maka dari itulah perlu adanya casting ke tipe `float64` pada tiap operand.
----
-
-## A.20.2. Pengisian Parameter Fungsi Variadic Menggunakan Data Slice
+## A.20.3. Pengisian Parameter Fungsi Variadic Menggunakan Data Slice
-Slice bisa digunakan sebagai parameter variadic. Caranya dengan menambahkan tanda titik tiga kali, tepat setelah nama variabel yang dijadikan parameter. Contohnya bisa dilihat pada kode berikut.
+Slice bisa digunakan sebagai argument pada fungsi variadic. Caranya penerapannya: tulis saja nama variabel tapi disertai dengan tanda titik tiga kali, dituliskan tepat setelah nama variabel yang dijadikan parameter. Contohnya bisa dilihat pada kode berikut:
```go
var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
@@ -90,9 +90,9 @@ var msg = fmt.Sprintf("Rata-rata : %.2f", avg)
fmt.Println(msg)
```
-Pada kode di atas, variabel `numbers` yang merupakan slice int, disisipkan ke fungsi `calculate()` sebagai parameter variadic (bisa dilihat tanda 3 titik setelah penulisan variabel). Teknik ini sangat berguna ketika sebuah data slice ingin difungsikan sebagai parameter variadic.
+Pada kode di atas, variabel `numbers` bertipe data slice int, disisipkan pada pemanggilan fungsi `calculate()` sebagai argument parameter fungsi variadic (bisa dilihat tanda 3 titik setelah penulisan variabel). Teknik ini sangat berguna pada case dimana sebuah data slice perlu untuk digunakan sebagai argument parameter variadic.
-Perhatikan juga kode berikut ini. Intinya adalah sama, hanya caranya yang berbeda.
+Agar lebih jelas, perhatikan 2 kode berikut. Intinya sama, hanya cara penulisannya yang berbeda.
```go
var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
@@ -105,7 +105,7 @@ var avg = calculate(2, 4, 3, 5, 4, 3, 3, 5, 5, 3)
Pada deklarasi parameter fungsi variadic, tanda 3 titik (`...`) dituliskan sebelum tipe data parameter. Sedangkan pada pemanggilan fungsi dengan menyisipkan parameter array, tanda tersebut dituliskan di belakang variabelnya.
-## A.20.3. Fungsi Dengan Parameter Biasa & Variadic
+## A.20.4. Fungsi Dengan Parameter Biasa & Variadic
Parameter variadic bisa dikombinasikan dengan parameter biasa, dengan syarat parameter variadic-nya harus diposisikan di akhir. Contohnya bisa dilihat pada kode berikut.
@@ -123,7 +123,7 @@ func yourHobbies(name string, hobbies ...string) {
Nilai parameter pertama fungsi `yourHobbies()` akan ditampung oleh `name`, sedangkan nilai parameter kedua dan seterusnya akan ditampung oleh `hobbies` sebagai slice.
-Cara pemanggilannya masih sama seperi pada fungsi biasa.
+Cara pemanggilannya masih sama seperi pada fungsi biasa, contoh:
```go
func main() {
@@ -131,7 +131,7 @@ func main() {
}
```
-Jika parameter kedua dan seterusnya ingin diisi dengan data dari slice, maka gunakan tanda titik tiga kali.
+Jika parameter kedua dan seterusnya ingin diisi dengan data dari slice, maka gunakan tanda titik tiga kali seperti ini:
```go
func main() {
diff --git a/content/A-fungsi.md b/content/A-fungsi.md
index b6c10fade..dc748eec4 100644
--- a/content/A-fungsi.md
+++ b/content/A-fungsi.md
@@ -1,18 +1,20 @@
# A.18. Fungsi
-Fungsi merupakan aspek penting dalam pemrograman. Definisi fungsi sendiri adalah sekumpulan blok kode yang dibungkus dengan nama tertentu. Penerapan fungsi yang tepat akan menjadikan kode lebih modular dan juga *dry* (kependekan dari *don't repeat yourself*), tak perlu menuliskan banyak kode yang kegunaannya berkali-kali, cukup sekali saja lalu panggil sesuai kebutuhan.
+Dalam konteks pemrograman, fungsi adalah sekumpulan blok kode yang dibungkus dengan nama tertentu. Penerapan fungsi yang tepat akan menjadikan kode lebih modular dan juga *dry* (singkatan dari *don't repeat yourself*) yang artinya kita tidak perlu menuliskan banyak kode untuk kegunaan yang sama berulang kali. Cukup deklarasikan sekali saja blok kode sebagai suatu fungsi, lalu panggil sesuai kebutuhan.
-Pada chapter ini kita akan belajar tentang penggunaan fungsi di Go.
+Pada chapter ini kita akan belajar tentang penerapannya di Go.
## A.18.1. Penerapan Fungsi
-Sebenarnya tanpa sadar, kita sudah menerapkan fungsi pada pembahasan-pembahasan sebelum ini, yaitu pada fungsi `main`. Fungsi `main` merupakan fungsi yang paling utama pada program Go.
+Mungkin pembaca sadar, bahwa sebenarnya kita sudah mengimplementasikan fungsi pada banyak praktek sebelumnya, yaitu fungsi `main()`. Fungsi `main()` sendiri merupakan fungsi utama pada program Go, yang akan dieksekusi ketika program dijalankan.
-Cara membuat fungsi cukup mudah, yaitu dengan menuliskan keyword `func`, diikuti setelahnya nama fungsi, kurung yang berisikan parameter, dan kurung kurawal untuk membungkus blok kode.
+Selain fungsi `main()`, kita juga bisa membuat fungsi lainnya. Dan caranya cukup mudah, yaitu dengan menuliskan keyword `func` kemudian diikuti nama fungsi, lalu kurung `()` (yang bisa diisi parameter), dan diakhiri dengan kurung kurawal untuk membungkus blok kode.
-Parameter sendiri adalah variabel yang disisipkan pada saat pemanggilan fungsi.
+Parameter merupakan variabel yang menempel di fungsi yang nilainya ditentukan saat pemanggilan fungsi tersebut. Parameter sifatnya opsional, suatu fungsi bisa tidak memiliki parameter, atau bisa saja memeliki satu atau banyak parameter (tergantung kebutuhan).
-Silakan lihat dan praktekan kode tentang implementasi fungsi berikut.
+> Data yang digunakan sebagai value parameter saat pemanggilan fungsi biasa disebut dengan argument parameter (atau argument).
+
+Agar lebih jelas, silakan lihat dan praktekan kode contoh implementasi fungsi berikut ini:
```go
package main
@@ -31,17 +33,22 @@ func printMessage(message string, arr []string) {
}
```
-Pada kode di atas, sebuah fungsi baru dibuat dengan nama `printMessage` memiliki 2 buah parameter yaitu string `message` dan slice string `arr`.
+Pada kode di atas, sebuah fungsi baru dibuat dengan nama `printMessage()` memiliki 2 buah parameter yaitu string `message` dan slice string `arr`.
+
+Fungsi tersebut dipanggil dalam `main()`, dalam pemanggilannya disisipkan dua buah argument parameter.
-Fungsi tersebut dipanggil dalam `main`, dengan disisipkan 2 buah data sebagai parameter, data pertama adalah string `"hallo"` yang ditampung parameter `message`, dan parameter ke 2 adalah slice string `names` yang nilainya ditampung oleh parameter `arr`.
+1. Argument parameter pertama adalah string `"halo"` yang ditampung parameter `message`
+2. Argument parameter ke-2 adalah slice string `names` yang nilainya ditampung oleh parameter `arr`
-Di dalam `printMessage`, nilai `arr` yang merupakan slice string digabungkan menjadi sebuah string dengan pembatas adalah karakter **spasi**. Penggabungan slice dapat dilakukan dengan memanfaatkan fungsi `strings.Join()` (berada di dalam package `strings`).
+Di dalam `printMessage()`, nilai `arr` yang merupakan slice string digabungkan menjadi sebuah string dengan pembatas adalah karakter **spasi**. Penggabungan slice dapat dilakukan dengan memanfaatkan fungsi `strings.Join()` (berada di dalam package `strings`).
![Contoh penggunaan fungsi](images/A_fungsi_1_function.png)
## A.18.2. Fungsi Dengan Return Value / Nilai Balik
-Sebuah fungsi bisa dirancang tidak mengembalikan nilai balik (*void*), atau bisa mengembalikan suatu nilai. Fungsi yang memiliki nilai kembalian, harus ditentukan tipe data nilai baliknya pada saat deklarasi.
+Selain parameter, fungsi bisa memiliki attribute **return value** atau nilai balik. Fungsi yang memiliki return value, saat deklarasinya harus ditentukan terlebih dahulu tipe data dari nilai baliknya.
+
+> Fungsi yang tidak mengembalikan nilai apapun (contohnya seperti fungsi `main()` dan `printMessage()`) biasa disebut dengan **void function**
Program berikut merupakan contoh penerapan fungsi yang memiliki return value.
@@ -61,8 +68,10 @@ func main() {
randomValue = randomWithRange(2, 10)
fmt.Println("random number:", randomValue)
+
randomValue = randomWithRange(2, 10)
fmt.Println("random number:", randomValue)
+
randomValue = randomWithRange(2, 10)
fmt.Println("random number:", randomValue)
}
@@ -73,41 +82,39 @@ func randomWithRange(min, max int) int {
}
```
-Fungsi `randomWithRange` bertugas untuk *generate* angka acak sesuai dengan range yang ditentukan, yang kemudian angka tersebut dijadikan nilai kembalian fungsi.
+Fungsi `randomWithRange()` didesain untuk *generate* angka acak sesuai dengan range yang ditentukan lewat parameter, yang kemudian angka tersebut dijadikan nilai balik fungsi.
![Fungsi dengan nilai balik](images/A_fungsi_2_function_return_type.png)
-Cara menentukan tipe data nilai balik fungsi adalah dengan menuliskan tipe data yang diinginkan setelah kurung parameter. Bisa dilihat pada kode di atas, bahwa `int` merupakan tipe data nilai balik fungsi `randomWithRange`.
+Cara menentukan tipe data nilai balik fungsi adalah dengan menuliskan tipe data yang diinginkan setelah kurung parameter. Bisa dilihat pada kode di atas, bahwa `int` merupakan tipe data nilai balik fungsi `randomWithRange()`.
```go
func randomWithRange(min, max int) int
```
-Sedangkan cara untuk mengembalikan nilai itu sendiri adalah dengan menggunakan keyword `return` diikuti data yang ingin dikembalikan. Pada contoh di atas, `return value` artinya nilai variabel `value` dijadikan nilai kembalian fungsi.
+Sedangkan cara untuk mengembalikan nilai itu sendiri adalah dengan menggunakan keyword `return` diikuti data yang dikembalikan. Pada contoh di atas, `return value` artinya nilai variabel `value` dijadikan nilai kembalian fungsi.
Eksekusi keyword `return` akan menjadikan proses dalam blok fungsi berhenti pada saat itu juga. Semua statement setelah keyword tersebut tidak akan dieksekusi.
----
-
Dari kode di atas mungkin ada beberapa hal yang belum pernah kita lakukan pada pembahasan-pembahasan sebelumnya, kita akan bahas satu-persatu.
## A.18.3. Penggunaan Fungsi `rand.New()`
-Fungsi ini digunakan untuk membuat object randomizer, yang dari object tersebut nilai random/acak bisa di-generate. Dalam penerapannya, fungsi `rand.New()` membutuhkan argument yaitu random source seed, yang bisa kita buat lewat statement `rand.NewSource(time.Now().Unix())`.
+Fungsi `rand.New()` digunakan untuk membuat object randomizer, yang dari object tersebut kita bisa mendapatkan nilai random/acak hasil generator. Dalam penerapannya, fungsi `rand.New()` membutuhkan argument yaitu random source seed, yang bisa kita buat lewat statement `rand.NewSource(time.Now().Unix())`.
```go
var randomizer = rand.New(rand.NewSource(time.Now().Unix()))
```
-> Dalam penggunaan fungsi `rand.NewSource`, argument bisa diisi dengan nilai apapun, salah satunya adalah `time.Now().Unix()`.
+> Dalam penggunaan fungsi `rand.NewSource()`, argument bisa diisi dengan nilai apapun, salah satunya adalah `time.Now().Unix()`.
>
-> Lebih detailnya mengenai random dibahas pada chapter [A.39. Random](A-random.html).
+> Lebih detailnya mengenai random dan apa peran seed dibahas pada chapter [A.39. Random](A-random.html).
-Fungsi `rand.New()` berada dalam package `math/rand`, yang harus di-import terlebih dahulu sebelum bisa dimanfaatkan. Package `time` juga perlu di-import karena kita menggunakan fungsi `(time.Now().Unix())` di situ.
+Fungsi `rand.New()` berada dalam package `math/rand`. Package tersebut harus di-import terlebih dahulu sebelum bisa menggunakan fungsi-fungsi yang ada didalamnya. Package `time` juga perlu di-import karena di contoh ini fungsi `(time.Now().Unix())` digunakan.
## A.18.4. Import Banyak Package
-Penulisan keyword `import` untuk banyak package bisa dilakukan dengan dua cara, dengan menuliskannya di tiap package, atau cukup sekali saja, bebas.
+Penulisan keyword `import` untuk banyak package bisa dilakukan dengan dua cara, dengan menuliskannya di tiap package, atau cukup sekali saja, bebas silakan pilih sesuai selera.
```go
import "fmt"
@@ -137,7 +144,7 @@ func randomWithRange(min, max int) int
## A.18.6. Penggunaan Keyword `return` Untuk Menghentikan Proses Dalam Fungsi
-Selain sebagai penanda nilai balik, keyword `return` juga bisa dimanfaatkan untuk menghentikan proses dalam blok fungsi di mana ia dipakai. Contohnya bisa dilihat pada kode berikut.
+Selain sebagai penanda nilai balik, keyword `return` juga bisa dimanfaatkan untuk menghentikan proses dalam blok fungsi di mana ia ditulis. Contohnya bisa dilihat pada kode berikut.
```go
package main
@@ -161,7 +168,7 @@ func divideNumber(m, n int) {
}
```
-Fungsi `divideNumber` dirancang tidak memiliki nilai balik. Fungsi ini dibuat untuk membungkus proses pembagian 2 bilangan, lalu menampilkan hasilnya.
+Fungsi `divideNumber()` dirancang tidak memiliki nilai balik. Fungsi ini dibuat untuk membungkus proses pembagian 2 bilangan, lalu menampilkan hasilnya.
Di dalamnya terdapat proses validasi nilai variabel pembagi, jika nilainya adalah 0, maka akan ditampilkan pesan bahwa pembagian tidak bisa dilakukan, lalu proses dihentikan pada saat itu juga (dengan memanfaatkan keyword `return`). Jika nilai pembagi valid, maka proses pembagian diteruskan.
diff --git a/content/A-go-command.md b/content/A-go-command.md
index 907786226..0e63aea79 100644
--- a/content/A-go-command.md
+++ b/content/A-go-command.md
@@ -1,14 +1,14 @@
# A.6. Command
-Pengembangan aplikasi Go tak jauh dari hal-hal yang berbau CLI atau *Command Line Interface*. Proses inisialisasi project, kompilasi, testing, eksekusi program, semuanya dilakukan lewat command line.
+Pengembangan aplikasi Go pastinya tak akan jauh dari hal-hal yang berbau CLI atau *Command Line Interface*. Di Go, proses inisialisasi project, kompilasi, testing, eksekusi program, semuanya dilakukan lewat command line.
Go menyediakan command `go`, dan pada chapter ini kita akan mempelajari beberapa di antaranya.
-> Pada pembelajaran chapter ini, pembaca tidak harus praktek, cukup pelajari saja untuk tahu. Mengenai praktek sendiri akan dimulai pada chapter selanjutnya, yaitu [A.7. Program Pertama: Hello World](/A-hello-world.html).
+> Pada pembelajaran chapter ini, pembaca tidak harus menghafal dan mempraktekan semuanya, cukup ikuti saja pembelajaran agar mulai familiar. Perihal prakteknya sendiri akan dimulai pada chapter selanjutnya, yaitu [A.7. Program Pertama: Hello World](/A-hello-world.html).
## A.6.1. Command `go mod init`
-*Command* `go mod init` digunakan untuk inisialisasi project pada Go (menggunakan Go Modules). Untuk nama project bisa menggunakan apapun, tapi umumnya adalah disamakan dengan nama direktori.
+*Command* `go mod init` digunakan untuk inisialisasi project pada Go yang menggunakan Go Modules. Untuk nama project bisa menggunakan apapun, tapi umumnya disamakan dengan nama direktori/folder.
Nama project ini penting karena nantinya berpengaruh pada *import path sub packages* yang ada dalam project tersebut.
@@ -20,7 +20,7 @@ go mod init
## A.6.2. Command `go run`
-*Command* `go run` digunakan untuk eksekusi file program (file ber-ekstensi `.go`). Cara penggunaannya dengan menuliskan *command* tersebut diikuti argumen nama file.
+*Command* `go run` digunakan untuk eksekusi file program, yaitu file yang ber-ekstensi `.go`. Cara penggunaannya dengan menuliskan *command* tersebut diikuti argumen nama file.
Berikut adalah contoh penerapan `go run` untuk eksekusi file program `main.go` yang tersimpan di path `project-pertama` yang path tersebut sudah diinisialisasi menggunakan `go mod init`.
@@ -31,7 +31,7 @@ go run main.go
![Eksekusi file program menggunakan `go run`](images/A_go_command_1_go_run.png)
-*Command* `go run` hanya bisa digunakan pada file yang nama package-nya adalah `main`. Lebih jelasnya dibahas pada chapter selanjutnya ([A.7. Program Pertama: Hello World](/A-hello-world.html)).
+*Command* `go run` hanya bisa digunakan pada file yang nama package-nya adalah `main`. Lebih jelasnya dibahas pada chapter selanjutnya, yaitu ([A.7. Program Pertama: Hello World](/A-hello-world.html)).
Jika ada banyak file yang package-nya `main` dan file-file tersebut berada pada satu direktori level dengan file utama, maka eksekusinya adalah dengan menuliskan semua file sebagai argument *command* `go run`. Contohnya bisa dilihat pada kode berikut.
@@ -39,11 +39,9 @@ Jika ada banyak file yang package-nya `main` dan file-file tersebut berada pada
go run main.go library.go
```
-> Lebih jelasnya perihal argument dan flag akan dibahas pada chapter [A.48. Arguments & Flag](/A-command-line-args-flag.html))
-
## A.6.3. Command `go test`
-Go menyediakan package `testing`, berguna untuk keperluan unit test. File yang akan di-test harus memiliki akhiran `_test.go`.
+Go menyediakan package `testing`, berguna untuk keperluan pembuatan file test. Pada penerapannya, ada aturan yang wajib diikuti yaitu nama file test harus berakhiran `_test.go`.
Berikut adalah contoh penggunaan *command* `go test` untuk testing file `main_test.go`.
@@ -57,50 +55,53 @@ go test main_test.go
*Command* ini digunakan untuk mengkompilasi file program.
-Sebenarnya ketika eksekusi program menggunakan `go run`, terjadi proses kompilasi juga. File hasil kompilasi akan disimpan pada folder temporary untuk selanjutnya langsung dieksekusi.
+Sebenarnya ketika eksekusi program menggunakan `go run` didalamnya terjadi proses kompilasi juga. File hasil kompilasi kemudian disimpan pada folder temporary untuk selanjutnya langsung dieksekusi.
-Berbeda dengan `go build`, *command* ini menghasilkan file *executable* atau *binary* pada folder yang sedang aktif. Contohnya bisa dilihat pada kode berikut.
+Berbeda dengan `go build`, *command* ini menghasilkan file *executable* atau *binary* pada folder yang sedang aktif. Contoh praktiknya bisa dilihat di bawah ini.
![Kompilasi file program menghasilkan file executable](images/A_go_command_4_go_build.png)
-Pada contoh di atas, project `project-pertama` di-build, menghasilkan file baru pada folder yang sama, yaitu `project-pertama.exe`, yang kemudian dieksekusi. *Default*-nya nama project akan otomatis dijadikan nama *binary*.
+Di contoh, project `project-pertama` di-build, hasilnya adalah file baru bernama `project-pertama.exe` berada di folder yang sama. File *executable* tersebut kemudian dieksekusi.
-Untuk nama executable sendiri bisa diubah menggunakan flag `-o`. Contoh:
+*Default* nama file binary atau executable adalah sesuai dengan nama project. Untuk mengubah nama file executable, gunakan flag `-o`. Contoh:
```
go build -o
go build -o program.exe
```
-> Untuk sistem operasi non-windows, tidak perlu menambahkan akhiran `.exe` pada nama *binary*
+> Khusus untuk sistem operasi non-windows, tidak perlu menambahkan akhiran `.exe` pada nama *binary*
## A.6.5. Command `go get`
-*Command* `go get` digunakan untuk men-download package. Sebagai contoh saya ingin men-download package Kafka driver untuk Go pada project `project-pertama`.
+*Command* `go get` digunakan untuk men-download package atau *dependency*. Sebagai contoh, penulis ingin men-download package Kafka driver untuk Go pada project `project-pertama`, maka command-nya kurang lebih seperti berikut:
```bash
cd project-pertama
go get github.com/segmentio/kafka-go
-dir
```
![Download package menggunakan `go get`](images/A_go_command_6_go_get.png)
-Pada contoh di atas, `github.com/segmentio/kafka-go` adalah URL package kafka-go. Package yang sudah terunduh tersimpan dalam temporary folder yang ter-link dengan project folder di mana *command* `go get` dieksekusi, menjadikan project tersebut bisa meng-*import* package terunduh.
+Pada contoh di atas, bisa dilihat bahwa URL `github.com/segmentio/kafka-go` merupakan URL package kafka-go. Package yang sudah terunduh tersimpan dalam temporary folder yang ter-link dengan project folder di mana *command* `go get` dieksekusi, menjadikan project tersebut bisa meng-*import* package yang telah di-download.
-Untuk mengunduh dependensi versi terbaru, gunakan flag `-u` pada command `go get`, misalnya:
+Untuk mengunduh package/dependency versi terbaru, gunakan flag `-u` pada command `go get`, contohnya:
```
go get -u github.com/segmentio/kafka-go
```
-Command `go get` **harus dijalankan dalam folder project**. Jika dijalankan di-luar project maka akan diunduh ke pada GOPATH.
+Command `go get` **harus dijalankan dalam folder project**. Jika dijalankan di-luar path project maka dependency yang ter-unduh akan ter-link dengan GOPATH, bukan dengan project.
+
+## A.6.6. Command `go mod download`
+
+*Command* `go mod download` digunakan untuk men-download dependency.
-## A.6.6. Command `go mod tidy`
+## A.6.7. Command `go mod tidy`
-*Command* `go mod tidy` digunakan untuk memvalidasi dependensi. Jika ada dependensi yang belum ter-download, maka akan otomatis di-download.
+*Command* `go mod tidy` digunakan untuk memvalidasi dependency sekaligus men-download-nya jika memang belum ter-download.
-## A.6.7. Command `go mod vendor`
+## A.6.8. Command `go mod vendor`
Command ini digunakan untuk vendoring. Lebih detailnya akan dibahas di akhir serial chapter A, pada chapter [A.61. Go Vendoring](/A-go-vendoring.html).
diff --git a/content/A-go-vendoring.md b/content/A-go-vendoring.md
index 35cb011aa..c2deefdb0 100644
--- a/content/A-go-vendoring.md
+++ b/content/A-go-vendoring.md
@@ -1,14 +1,14 @@
# A.61. Go Vendoring
-Pada bagian ini kita akan belajar cara pemanfaatan vendoring untuk menyimpan dependensi di lokal.
+Pada bagian ini kita akan belajar cara pemanfaatan vendoring untuk menyimpan copy dependency di lokal dalam folder project.
## A.61.1. Penjelasan
-Vendoring di Go merupakan kapabilitas untuk mengunduh semua dependency atau *3rd party*, untuk disimpan di lokal dalam folder project, dalam folder bernama `vendor`.
+Vendoring di Go memberikan kita kapabilitas untuk mengunduh semua dependency atau *3rd party*, untuk disimpan di lokal dalam folder project, dalam subfolder bernama `vendor`.
-Dengan adanya folder tersebut, maka Go tidak akan *lookup* 3rd party ke cache folder, melainkan langsung mempergunakan yang ada dalam folder `vendor`. Jadi tidak perlu download lagi dari internet.
+Dengan adanya folder tersebut, maka Go tidak akan *lookup* 3rd party ke cache folder ataupun ke GOPATH, melainkan langsung mengambil dari yang ada dalam folder `vendor`. Jadi kalau dependency sudah ada di dalam `vendor`, maka kita tidak perlu download lagi dari internet menggunakan command `go mod download` ataupun `go mod tidy`.
-Ok lanjut.
+Ok lanjut ke praktek ya.
## A.61.2. Praktek Vendoring
@@ -38,7 +38,7 @@ func main() {
}
```
-Setelah itu jalankan command `go mod vendor` untuk vendoring *3rd party library* yang dipergunakan, dalam contoh ini adlah gubrak.
+Setelah itu jalankan command `go mod vendor` untuk vendoring *3rd party library* yang dipergunakan, dalam contoh ini adalah gubrak.
![Vendoring](images/A_go_vendoring_1_vendor.png)
@@ -46,14 +46,7 @@ Bisa dilihat, sekarang library gubrak *source code*-nya disimpan dalam folder `v
## A.61.3 Build dan Run Project yang Menerapkan Vendoring
-Untuk membuat proses build lookup ke folder vendor, kita tidak perlu melakukan apa-apa, setidaknya jika versi Go yang diinstall adalah 1.14 ke atas. Maka command build maupun run masih sama.
-
-```
-go run main.go
-go build -o executable
-```
-
-Untuk yg menggunakan versi Go di bawah 1.14, penulis sarankan untuk upgrade. Atau bisa gunakan flag `-mod=vendor` untuk memaksa Go lookup ke folder `vendor`.
+Cara agar Go lookup ke folder `vendor` saat build adalah dengan menambahkan flag `-mod=vendor` sewaktu build atau run project.
```
go run -mod=vendor main.go
@@ -62,11 +55,9 @@ go build -mod=vendor -o executable
## A.61.3. Manfaat Vendoring
-Manfaat vendoring adalah pada sisi kompatibilitas dan kestabilan 3rd party. Jadi dengan vendor, misal 3rd party yang kita gunakan di itu ada update yg sifatnya tidak *backward compatible*, maka aplikasi kita tetap aman karena menggunakan yang ada dalam folder `vendor`.
-
-Jika tidak menggunakan vendoring, maka bisa saja saat `go mod tidy` sukses, namun sewaktu build error, karena ada fungsi yg tidak kompatibel lagi misalnya.
+Manfaat vendoring adalah pada sisi kompatibilitas & kestabilan 3rd party, selain itu kita tidak perlu repot mendownload dependency karena semuanya sudah ada di lokal.
-Untuk penggunaan vendor apakah wajib? menurut saya tidak. Sesuaikan kebutuhan saja.
+Konsekuensi penerapan vendoring adalah size project menjadi cukup besar. Untuk penggunaan vendor apakah wajib? menurut saya tidak. Sesuaikan kebutuhan saja.
---
diff --git a/content/A-golang-generics.md b/content/A-golang-generics.md
index 8af491f11..e79828b1a 100644
--- a/content/A-golang-generics.md
+++ b/content/A-golang-generics.md
@@ -1,20 +1,20 @@
# A.65. Go Generics
-Pada chapter ini kita akan belajar tentang Generics di Go.
+Pada chapter ini kita akan belajar tentang penerapan Generics di Go.
## A.65.1. Konsep Generic Programming
-Generic Programming adalah salah satu metode dalam penulisan kode program, di mana tipe data dalam kode didefinisikan menggunakan tipe data yang tipe pastinya adalah dituliskan belakangan saat kode tersebut di-call atau dieksekusi. Konsep ini sudah cukup umum terutama pada bahasa yang static type.
+Generic Programming adalah salah satu metode dalam penulisan kode program, di mana tipe data dalam kode didefinisikan menggunakan suatu tipe yang tipe pastinya ditulis belakangan saat kode tersebut di-call atau dieksekusi. Konsep generic ini cukup umum diterapkan terutama pada bahasa pemrograman yang mengadopsi static typing.
-Di Go, kita punya tipe `interface{}` yang biasa difungsikan sebagai tipe untuk menampung data yang tidak pasti tipe datanya. Generic dan `interface{}` berbeda. Tipe `interface{}` akan membungkus data aslinya atau *underlying value*-nya, dan untuk mengakses data tersebut, kita perlu menerapkan *type assertion*, contohnya `data.(int)`.
+Di Go, kita punya tipe `any` atau `interface{}` yang biasa difungsikan sebagai penampung data yang tidak pasti tipe datanya. Generic berbeda dibanding `any`. Tipe `any` dalam prakteknya membungkus data asli atau *underlying value*-nya, dengan pengaksesan data asli tersebut dilakukan via metode *type assertion*, contohnya `data.(int)`.
-Berbeda dibanding `interface{}`, pada penggunaan generic kita perlu mendefinisikan cakupan tipe data yang kompatibel untuk dipakai saat pemanggilan kode, atau bisa juga menggunakan keyword `comparable`, yang artinya tipe data adalah kompatibel dengan tipe apapun.
+Berbeda dibanding `any`, pada Generic kita perlu mendefinisikan cakupan tipe data yang kompatibel untuk digunakan saat pemanggilan kode.
-Ok, mari kita lanjut ke pembahasan yang lebih teknis agar tidak bingung.
+Ok, mari kita lanjut ke praktek saja agar tidak makin bingung.
## A.65.2. Penerapan Generic pada Fungsi
-Mari kita mulai pembelajaran dengan kode di bawah ini:
+Mari kita mulai pembelajaran dengan kode sederhana berikut:
```go
package main
@@ -37,9 +37,11 @@ func main() {
Pada kode di atas, didefinisikan sebuah fungsi `Sum()` yang tugasnya menghitung total atau *summary* dari data slice numerik yang disisipkan di parameter. Dalam `main()`, kita panggil fungsi tersebut untuk menghitung total dari sejumlah data dengan tipe `[]int`. Saya rasa sampai sini cukup jelas.
-Fungsi `Sum()` memiliki satu limitasinya, yaitu hanya bisa digunakan pada data yang tipenya `[]int`, tidak bisa untuk tipe slice numerik lain. Bagaimana jika menggunakan tipe `interface{}`? apakah bisa? bisa saja sebenarnya, tapi pastinya lebih report karena sulit untuk menerapkan *type assertion* kalau tidak tau tipe pasti parameter `numbers` itu apa. Penggunaan `interface{}` perlu dibarengi dengan penerapan [reflection API](/A-reflect.html).
+Fungsi `Sum()` memiliki satu limitasinya, yaitu hanya bisa digunakan pada data yang tipenya `[]int`, tidak bisa untuk tipe slice numerik lain. Bagaimana jika menggunakan tipe `interface{}`? apakah bisa? bisa saja sebenarnya, tapi pastinya lebih report karena sulit untuk menerapkan *type assertion* kalau tidak tau pasti cakupan tipe yang di-support oleh parameter `numbers` itu apa saja.
-Di sini kita bisa terapkan Generic, kita akan modifikasi fungsi di atas agar bisa menampung tipe data slice numerik lainnya diluar `[]int`.
+> Alternatifnya, penggunaan `interface{}` bisa dibarengi dengan penerapan [reflection API](/A-reflect.html).
+
+Nah, agar tidak repot, di sini kita akan terapkan Generic. Kode akan dimodifikasi atas agar bisa menampung tipe data slice numerik lainnya diluar tipe `[]int`.
Ok, sekarang ubah kode fungsi `Sum` menjadi seperti di bawah ini:
@@ -93,11 +95,13 @@ func main() {
}
```
+Output program:
+
![Golang generic](images/A_generics_1.png)
## A.65.3. Comparable Data Type pada Fungsi Generic
-Selanjutnya kita modifikasi lagi fungsi `Sum` agar tipe kompatibel `V` di sini kompatibel dengan tipe numerik lainnya seperti `float64`. Caranya sangat mudah, cukup tambahkan tipe datanya pada statement `V int` dengan delimiter pipe (`|`).
+Selanjutnya, modifikasi lagi fungsi `Sum` agar tipe kompatibel `V` di sini bisa kompatibel dengan tipe numerik lainnya seperti `float64`. Caranya sangat mudah, cukup tambahkan tipe data yang diinginkan untuk kompatibel pada statement `V int` menggunakan delimiter pipe (`|`).
```go
func Sum[V int | float32 | float64](numbers []V) V {
@@ -126,7 +130,7 @@ fmt.Println("total:", total3)
![Golang generic](images/A_generics_2.png)
-Nice, hasilnya sesuai harapan. Sampai sini kita sudah paham bagaimana cara pendefinisian tipe kompatibel pada fungsi dan cara pemanfaatannya.
+Jos gandos, hasilnya sesuai harapan. Sampai sini kita sudah paham bagaimana cara pendefinisian tipe kompatibel pada fungsi dan cara pemanfaatannya.
## A.65.4. Tipe Argumen Saat Pemanggilan Fungsi Generic
@@ -146,15 +150,15 @@ Sum[float32]([]float32{2.5, 7.2})
Sum[float64]([]float64{1.23, 6.33, 12.6})
```
-Di case ini (dan banyak case lainnya), tipe data kompatibel tidak perlu dituliskan secara eksplisit karena secara cerdas kompiler bisa mendeteksi tipe yang kompatibel berdasarkan tipe data parameter saat pemanggilan fungsi.
+Di case ini (dan banyak case lainnya), tipe data yang sudah kompatibel tidak perlu dituliskan secara eksplisit karena kompiler secara cerdas bisa mendeteksi tipe yang kompatibel berdasarkan tipe data parameter saat pemanggilan fungsi.
## A.65.5. Keyword `comparable`
-Sekarang kita akan belajar kegunaan satu keyword penting, yaitu `comparable`. Keyword tersebut merupakan tipe data yang kompatibel dengan semua tipe yang ada.
+Sekarang kita akan belajar kegunaan satu keyword penting lainnya, yaitu `comparable`. Keyword ini merepresentasikan semua tipe data yang kompatibel.
Pada kode di atas kita menggunakan `V int | float32 | float64` untuk mendefinisikan tipe yang kompatibel dengan tipe `int`, `float32`, dan `float64`. Jika ingin membuat tipe `V` kompatibel dengan banyak tipe lainnya, tambahkan saja tipe2 yang diinginkan. Atau, jika ingin kompatibel dengan **semua tipe data** maka gunakan `comparable`, penulisannya menjadi `V comparable`.
-Ok, mari kita coba terapkan. O iya, sebelum mulai, agar pembaca makin paham perihal fungsi generic, kita siapkan 2 fungsi yang mirip berikut:
+Ok, mari kita coba terapkan. Kita tidak akan menerapkan `comparable` pada contoh di atas karena fungsi `Sum()` kita desain untuk komputasi nilai numerik. Jika `comparable` diterapkan disitu jadinya kurang pas. Oleh karena itu kita siapkan 2 fungsi baru yang mirip berikut sebagai bahan praktek selanjutnya.
```go
func SumNumbers1(m map[string]int64) int64 {
@@ -183,16 +187,16 @@ func main() {
}
```
-Dua fungsi di atas mirip, tapi memiliki beberapa perbedaan:
+Dua fungsi di atas mirip, tapi memiliki beberapa perbedaan yaitu:
-1. Penulisan `SumNumbers1` adalah non-generic, sedangkan `SumNumbers2` adalah generic.
-2. Pada `SumNumbers1`, kita menggunakan kombinasi dua tipe data untuk membentuk `map`, yaitu `string` sebagai map key dan `int64` sebagai map value.
-3. Pada `SumNumbers2`, kita breakdown pendefinisian tipe data map menjadi lebih mendetail:
+1. Penulisan `SumNumbers1()` adalah non-generic, sedangkan `SumNumbers2()` adalah generic.
+2. Pada `SumNumbers1()`, kita menggunakan kombinasi dua tipe data untuk membentuk `map`, yaitu `string` sebagai map key dan `int64` sebagai map value.
+3. Pada `SumNumbers2()`, kita breakdown pendefinisian tipe data map menjadi lebih mendetail:
- Tipe map key adalah `K` yang tipe datanya kompatibel dengan semua tipe data.
- Tipe map value adalah `V` yang tipe datanya kompatibel dengan `int64` dan `float64`.
- Yang sebelumnya `map[string]int64` kini menjadi `map[K]V`.
-Karena `SumNumbers2` menggunakan generic, maka fungsi ini mendukung sangat banyak tipe data karena menggunakan kombinasi dari tipe `K` yang kompatibel dengan semua tipe; dan tipe `V` yang kompatibel dengan `int64` dan `float64`.
+Karena `SumNumbers2()` menggunakan generic, maka fungsi ini mendukung sangat banyak tipe data karena menggunakan kombinasi dari tipe `K` yang kompatibel dengan semua tipe; dan tipe `V` yang kompatibel dengan `int64` dan `float64`.
- `map[string]int64`
- `map[interface{}]int64`
@@ -206,7 +210,7 @@ Jalankan kode, lihat hasilnya.
## A.65.6. Generic *Type Constraint*
-Selanjutnya buat fungsi `SumNumbers3`, isinya kurang lebih sama, hanya saja pada tipe data generic kita tidak menggunakan `V int64 | float64`, yang digunakan adalah `Number` yang merupakan tipe data baru (generic *type constraint*).
+Selanjutnya buat fungsi `SumNumbers3()` yang isinya kurang adalah lebih sama. Kali ini kita tidak menggunakan `V int64 | float64`, melainkan menggunakan tipe `Number` yang merupakan tipe data baru yang akan kita buat juga (generic *type constraint*).
```go
type Number interface {
@@ -222,15 +226,15 @@ func SumNumbers3[K comparable, V Number](m map[K]V) V {
}
```
-Cara pendefinisian generic *type constraint* adalah seperti pendefinisan tipe data kustom menggunakan keyword `type`, bedanya adalah di sini `interface{}` dipergunakan sebagai tipe, dan di dalamnya di-embed 2 tipe yang diinginkan untuk menjadi *comparable type*, yaitu `int64` dan `float64`. Dari sini, selanjutnya tipe `Number` bisa dimanfaatkan sebagai tipe data kompatibel dalam generic.
+Cara pendefinisian generic *type constraint* adalah seperti pendefinisan tipe data kustom menggunakan keyword `type`, bedanya adalah di sini `interface{}` dipergunakan sebagai tipe, yang di dalamnya di-embed 2 tipe yang diinginkan untuk menjadi *comparable type*, yaitu `int64` dan `float64`. Hasilnya, tipe `Number` bisa dimanfaatkan dalam penerapan generic sebagai tipe data yang kompatibel.
> Perlu diketahui, tipe yang didefinisikan menggunakan *type constraint* ini hanya bisa dimanfaatkan pada generic. Tipe jenis ini tidak bisa digunakan di luar scope kode generic. Sebagai contoh, coba deklarasikan `var s Number` dalam fungsi `main()`, hasilnya akan muncul syntax error.
-Ok, sekarang mari ubah pemanggilan fungsi `SumNumbers2` pada main menjadi `SumNumbers3` dan lihat hasilnya, jalan.
+Ok, sekarang ubah pemanggilan fungsi `SumNumbers2()` pada main menjadi `SumNumbers3()` lalu coba jalankan dan lihat hasilnya, pasti outputnya sama, menandakan bahwa kode program berjalan sesuai desain.
## A.65.7. Struct Generic
-Generic juga bisa diterapkan pada pendefinisian struct, contohnya seperti berikut:
+Generic juga bisa diterapkan pada struct, contohnya:
```go
type UserModel[T int | float64] struct {
@@ -259,7 +263,7 @@ func main() {
}
```
-Pada penulisan struct, sisipkan notasi generic. Lalu pada deklarasi variabel object, tulis secara eksplisit tipe data untuk variabel kompatibel.
+Cukup tuliskan notasi generic pada deklarasi struct. Kemudian siapkan variabel object, tulis secara eksplisit tipe data untuk variabel kompatibel.
![Golang generic](images/A_generics_4.png)
@@ -267,9 +271,7 @@ Pada penulisan struct, sisipkan notasi generic. Lalu pada deklarasi variabel obj
Sampai artikel ini ditulis, generic tidak bisa diterapkan pada method (meski bisa diterapkan pada fungsi)
----
-
-Ok, sekian pembahasan mengenai generics. Jika ada update perihal generic API akan penulis update ke chapter ini juga.
+> Penulis akan update konten chapter ini jika ada update pada spesifikasi generic API.
---
diff --git a/content/A-gopath-dan-workspace.md b/content/A-gopath-dan-workspace.md
index eee21cc12..c266f6bbe 100644
--- a/content/A-gopath-dan-workspace.md
+++ b/content/A-gopath-dan-workspace.md
@@ -1,10 +1,16 @@
-# A.4. GOPATH Dan Workspace
+# A.4. GOPATH dan Workspace
-> PERINGATAN! Setup Go project menggunakan GOPATH kurang dianjurkan untuk Go versi terbaru. Lebih baik gunakan [A.3. Setup Go Modules](/A-setup-go-project-dengan-go-modules.html). Tapi meski demikian, bukan berarti GOPATH tidak berguna sama sekali, jadi silakan ikuti panduan berikut jika mau.
+Pada chapter ini kita akan belajar tentang apa itu GOPATH beserta cara setupnya.
+
+> ⚠️ INFORMASI ⚠️
+>
+> Setup Go project menggunakan GOPATH kurang dianjurkan untuk Go versi terbaru. Lebih baik gunakan [A.3. Setup Go Modules](/A-setup-go-project-dengan-go-modules.html).
+>
+> Namun meski demikian, bukan berarti GOPATH tidak berguna sama sekali, jadi silakan ikuti panduan berikut jika diperlukan.
## A.4.1. Variabel `GOPATH`
-**GOPATH** adalah variabel yang digunakan oleh Go sebagai rujukan lokasi di mana semua folder project disimpan, kecuali untuk yg diinisialisasi menggunakan Go Modules. GOPATH berisikan 3 buah sub-folder: `src`, `bin`, dan `pkg`.
+**GOPATH** adalah variabel yang digunakan oleh Go sebagai rujukan lokasi di mana semua folder project disimpan (kecuali untuk yg diinisialisasi menggunakan Go Modules). GOPATH berisikan 3 buah sub-folder: `src`, `bin`, dan `pkg`.
Project di Go bisa ditempatkan dalam `$GOPATH/src`. Sebagai contoh anda ingin membuat project dengan nama `belajar`, maka **harus** dibuatkan sebuah folder dengan nama `belajar`, ditempatkan dalam `src` (`$GOPATH/src/belajar`).
diff --git a/content/A-goroutine.md b/content/A-goroutine.md
index 653650da0..758b71cc0 100644
--- a/content/A-goroutine.md
+++ b/content/A-goroutine.md
@@ -1,8 +1,10 @@
# A.30. Goroutine
-Goroutine mirip dengan thread, tapi sebenarnya bukan. Sebuah *native thread* bisa berisikan sangat banyak goroutine. Mungkin lebih pas kalau goroutine disebut sebagai **mini thread**. Goroutine sangat ringan, hanya dibutuhkan sekitar **2kB** memori saja untuk satu buah goroutine. Eksekusi goroutine bersifat *asynchronous*, menjadikannya tidak saling tunggu dengan goroutine lain.
+Goroutine secara konsep mirip seperti *thread*, meskipun sebenarnya berbeda. Sebuah *native thread* bisa berisikan sangat banyak goroutine. Mungkin lebih pas kalau goroutine disebut sebagai **mini thread**. Goroutine sangat ringan, hanya dibutuhkan sekitar **2kB** memori saja untuk satu buah goroutine. Eksekusi goroutine bersifat *asynchronous*, menjadikannya tidak saling tunggu dengan goroutine lain.
> Karena goroutine sangat ringan, maka eksekusi banyak goroutine bukan masalah. Akan tetapi jika jumlah goroutine sangat banyak sekali (contoh 1 juta goroutine dijalankan pada komputer dengan RAM terbatas), memang proses akan jauh lebih cepat selesai, tapi memory/RAM pasti bengkak.
+>
+> Selain itu, dalam pengaplikasiannya jangan hanya terpaku pada size goroutine yang kecil tersebut, tapi pertimbangkan juga kode/proses/logic yang dibuat di dalam goroutine itu sekompleks apa, karena hal tersebut sangat berpengaruh dengan konsumsi resource hardware.
Goroutine merupakan salah satu bagian paling penting dalam *concurrent programming* di Go. Salah satu yang membuat goroutine sangat istimewa adalah eksekusi-nya dijalankan di multi core processor. Kita bisa tentukan berapa banyak core yang aktif, makin banyak akan makin cepat.
@@ -12,7 +14,7 @@ Mulai chapter **A.29** ini hingga **A.34**, lalu dilanjut **A.56** dan **A.57**,
## A.30.1. Penerapan Goroutine
-Untuk menerapkan goroutine, proses yang akan dieksekusi sebagai goroutine harus dibungkus ke dalam sebuah fungsi. Pada saat pemanggilan fungsi tersebut, ditambahkan keyword `go` di depannya, dengan itu goroutine baru akan dibuat dengan tugas adalah menjalankan proses yang ada dalam fungsi tersebut.
+Untuk menerapkan goroutine, proses yang akan dieksekusi sebagai goroutine harus dibungkus ke dalam sebuah fungsi, ini hukumnya wajib. Kemudian nantinya saat pemanggilan fungsi, tambahkan keyword `go` di depannya, dengan ini maka goroutine baru dibuat dengan tugas adalah menjalankan proses yang ada dalam fungsi tersebut.
Berikut merupakan contoh implementasi sederhana tentang goroutine. Program di bawah ini menampilkan 10 baris teks, 5 dieksekusi dengan cara biasa, dan 5 lainnya dieksekusi sebagai goroutine baru.
@@ -45,23 +47,25 @@ Pembuatan goroutine baru ditandai dengan keyword `go`. Contohnya pada statement
Fungsi `fmt.Scanln()` mengakibatkan proses jalannya aplikasi berhenti di baris itu (**blocking**) hingga user menekan tombol enter. Hal ini perlu dilakukan karena ada kemungkinan waktu selesainya eksekusi goroutine `print()` lebih lama dibanding waktu selesainya goroutine utama `main()`, mengingat bahwa keduanya sama-sama asnychronous. Jika itu terjadi, goroutine yang belum selesai secara paksa dihentikan prosesnya karena goroutine utama sudah selesai dijalankan.
+Output program:
+
![Implementasi goroutine](images/A_goroutine_1_goroutine.png)
-Bisa dilihat di output, tulisan `"halo"` dan `"apa kabar"` bermunculan selang-seling. Ini disebabkan karena statement `print(5, "halo")` dijalankan sebagai goroutine baru, menjadikannya tidak saling tunggu dengan `print(5, "apa kabar")`.
+Bisa dilihat di output, tulisan `"halo"` dan `"apa kabar"` bermunculan selang-seling. Ini disebabkan karena statement `print(5, "halo")` dijalankan sebagai goroutine, menjadikannya tidak saling tunggu dengan `print(5, "apa kabar")`.
Pada gambar di atas, program dieksekusi 2 kali. Hasil eksekusi pertama berbeda dengan kedua, penyebabnya adalah karena kita menggunakan 2 prosesor. Goroutine mana yang dieksekusi terlebih dahulu tergantung kedua prosesor tersebut.
----
+## A.30.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan tentang beberapa fungsi yang baru kita pelajari di atas.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.30.1.1. Penggunaan Fungsi `runtime.GOMAXPROCS()`
+#### ◉ Penggunaan Fungsi `runtime.GOMAXPROCS()`
Fungsi ini digunakan untuk menentukan jumlah core atau processor yang digunakan dalam eksekusi program.
Jumlah yang diinputkan secara otomatis akan disesuaikan dengan jumlah asli *logical processor* yang ada. Jika jumlahnya lebih, maka dianggap menggunakan sejumlah prosesor yang ada.
-## A.30.1.2. Penggunaan Fungsi `fmt.Scanln()`
+#### ◉ Penggunaan Fungsi `fmt.Scanln()`
Fungsi ini akan meng-capture semua karakter sebelum user menekan tombol enter, lalu menyimpannya pada variabel.
diff --git a/content/A-hash-sha1.md b/content/A-hash-sha1.md
index c6128c378..213fdce2d 100644
--- a/content/A-hash-sha1.md
+++ b/content/A-hash-sha1.md
@@ -1,6 +1,6 @@
# A.47. Hash SHA1
-Hash adalah algoritma enkripsi untuk mengubah text menjadi deretan karakter acak. Jumlah karakter hasil hash selalu sama. Hash termasuk *one-way encryption*, membuat hasil dari hash tidak bisa dikembalikan ke text asli.
+Hash adalah algoritma enkripsi satu arah untuk mengubah text menjadi deretan karakter acak. Jumlah karakter hasil hash selalu sama. Hash termasuk *one-way encryption*, hasil dari hash tidak bisa dikembalikan ke text asli.
SHA1 atau **Secure Hash Algorithm 1** merupakan salah satu algoritma hashing yang sering digunakan untuk enkripsi data. Hasil dari sha1 adalah data dengan lebar **20 byte** atau **160 bit**, biasa ditampilkan dalam bentuk bilangan heksadesimal 40 digit.
@@ -55,7 +55,6 @@ import "time"
func doHashUsingSalt(text string) (string, string) {
var salt = fmt.Sprintf("%d", time.Now().UnixNano())
var saltedText = fmt.Sprintf("text: '%s', salt: %s", text, salt)
- fmt.Println(saltedText)
var sha = sha1.New()
sha.Write([]byte(saltedText))
var encrypted = sha.Sum(nil)
@@ -89,7 +88,7 @@ func main() {
}
```
-Hasil ekripsi fungsi `doHashUsingSalt` akan selalu beda, karena salt yang digunakan adalah waktu.
+Hasil ekripsi fungsi `doHashUsingSalt()` akan selalu beda, karena salt yang digunakan adalah waktu.
![Hashing dengan salt](images/A_hash_2_hash_salt_sha1.png)
diff --git a/content/A-hello-world.md b/content/A-hello-world.md
index 7718ab007..df9f388b5 100644
--- a/content/A-hello-world.md
+++ b/content/A-hello-world.md
@@ -1,8 +1,8 @@
# A.7. Program Pertama: Hello World
-Semua persiapan sudah selesai, saatnya masuk pada sesi programming. Program pertama yang akan kita buat adalah aplikasi kecil yang menampilkan text **Hello world**.
+Semua persiapan sudah selesai, saatnya masuk pada sesi programming. Program pertama yang akan kita buat adalah cukup terkenal di kalangan programmer, yaitu program untuk memunculkan text **Hello world**.
-Pada chapter ini akan dijelaskan secara komprehensif *step-by-step* mulai dari awal. Mulai dari pembuatan project, pembuatan file program, sesi penulisan kode (coding), hingga eksekusi program.
+Proses pembelajaran di chapter ini akan disampaikan secara runtun dan komprehensif, *step-by-step* mulai dari awal. Mulai dari pembuatan project, pembuatan file program, sesi penulisan kode (coding), hingga eksekusi program.
## A.7.1. Inisialisasi Project
@@ -24,7 +24,7 @@ Buka editor, di sini penulis menggunakan VSCode. Cari menu untuk menambahkan pro
## A.7.3. Menyiapkan File Program
-File program di sini maksudnya adalah file yang isinya *source code* Go. File ini berekstensi `.go`.
+File program di sini maksudnya adalah file yang isinya *source code* Go. Ciri khas file program adalah memiliki ekstensi `.go`.
Di dalam project yang telah dibuat, siapkan sebuah file dengan nama bebas, yang jelas harus ber-ekstensi `.go`. Pada contoh ini saya menggunakan nama file `main.go`.
@@ -34,9 +34,9 @@ Pembuatan file program bisa dilakukan lewat CLI atau browser, atau juga lewat ed
## A.7.4. Program Pertama: Hello Word
-Setelah project folder dan file program sudah siap, saatnya untuk *programming*.
+Setelah project folder dan file program sudah siap, saatnya untuk *coding*.
-Di bawah ini merupakan contoh kode program sederhana untuk memunculkan text **Hello world** ke layar output command prompt. Silakan salin kode berikut ke file program yang telah dibuat. Sebisa mungkin jangan copy paste. Biasakan untuk menulis dari awal, agar cepat terbiasa dan familiar dengan Go.
+Silakan salin kode berikut ke file program yang telah dibuat. Sebisa mungkin jangan copy paste. Biasakan untuk menulis dari awal, agar cepat terbiasa dan familiar dengan Go.
```go
package main
diff --git a/content/A-instalasi-editor.md b/content/A-instalasi-editor.md
index c18c770d0..9e8e55f1b 100644
--- a/content/A-instalasi-editor.md
+++ b/content/A-instalasi-editor.md
@@ -1,11 +1,9 @@
# A.5. Instalasi Editor
-Proses pembuatan aplikasi menggunakan Go akan lebih maksimal jika didukung oleh editor atau **IDE** yang pas. Ada cukup banyak pilihan bagus yang bisa dipertimbangkan, di antaranya: Brackets, JetBrains GoLand, Netbeans, Atom, Visual Studio Code, Sublime Text, dan lainnya.
+Proses pembuatan aplikasi menggunakan Go akan lebih maksimal jika didukung oleh editor atau **IDE** yang pas. Ada cukup banyak pilihan bagus yang bisa dipertimbangkan, di antaranya: JetBrains GoLand, Visual Studio Code, Netbeans, Atom, Sublime Text, dan lainnya.
Penulis sarankan untuk memilih editor yang paling nyaman digunakan, preferensi masing-masing pastinya berbeda. Penulis sendiri lebih sering menggunakan **Visual Studio Code**. Editor ini sangat ringan, mudah didapat, dan memiliki ekstensi yang bagus untuk bahasa Go. Jika pembaca ingin menggunakan editor yang sama, maka silakan melanjutkan panduan berikut.
-Pada chapter ini akan dijelaskan bagaimana cara instalasi editor Visual Studio Code.
-
## A.5.1. Instalasi Editor Visual Studio Code
1. Download Visual Studio Code di [https://code.visualstudio.com/Download](https://code.visualstudio.com/Download), pilih sesuai dengan sistem operasi yang digunakan.
diff --git a/content/A-interface-kosong.md b/content/A-interface-kosong.md
index cb8ab83e9..ca9e8edbb 100644
--- a/content/A-interface-kosong.md
+++ b/content/A-interface-kosong.md
@@ -1,10 +1,12 @@
-# A.28. Interface Kosong (Any)
+# A.28. Any / interface{} / Interface Kosong
-Interface kosong atau *empty interface* yang dinotasikan dengan `interface{}` atau `any`, merupakan tipe data yang sangat spesial. Variabel bertipe ini bisa menampung segala jenis data, bahkan array, pointer, apapun. Tipe data dengan konsep ini biasa disebut dengan **dynamic typing**.
+Interface kosong atau *empty interface* yang dinotasikan dengan `interface{}` atau `any`, merupakan tipe data yang sangat spesial karena variabel bertipe ini bisa menampung segala jenis data, baik itu numerik, string, bahkan array, pointer, apapun.
-## A.28.1. Penggunaan `interface{}`
+> Dalam konsep pemrograman umum, konsep variabel yang bisa menampung banyak jenis tipe data disebut dengan **dynamic typing**.
-`interface{}` merupakan tipe data, sehingga cara penggunaannya sama seperti pada tipe data lainnya, hanya saja nilai yang diisikan bisa apa saja. Contoh:
+## A.28.1. Penggunaan `any` / `interface{}`
+
+`any` atau `interface{}` merupakan tipe data, sehingga cara penggunaannya sama seperti tipe data pada umumnya, perbedaannya pada variabel bertipe ini nilainya bisa diisi dengan apapun. Contoh:
```go
package main
@@ -61,9 +63,9 @@ data = map[string]any{
}
```
-## A.28.3. Casting Variabel Interface Kosong
+## A.28.3. Casting Variabel Any / Interface Kosong
-Variabel bertipe `interface{}` bisa ditampilkan ke layar sebagai `string` dengan memanfaatkan fungsi print, seperti `fmt.Println()`. Tapi perlu diketahui bahwa nilai yang dimunculkan tersebut bukanlah nilai asli, melainkan bentuk string dari nilai aslinya.
+Variabel bertipe `interface{}` bisa ditampilkan ke layar sebagai `string` dengan memanfaatkan fungsi print, seperti `fmt.Println()`. Tapi perlu diketahui bahwa nilai yang dimunculkan tersebut bukanlah nilai asli, melainkan bentuk text dari nilai aslinya.
Hal ini penting diketahui, karena untuk melakukan operasi yang membutuhkan nilai asli pada variabel yang bertipe `interface{}`, diperlukan casting ke tipe aslinya. Contoh seperti pada kode berikut.
@@ -92,7 +94,7 @@ Pada contoh kedua, `secret` berisikan array string. Kita memerlukan string terse
![Casting pada variabel bertipe `interface{}`](images/A_interface_kosong_2_interface_casting.png)
-Teknik casting pada interface disebut dengan **type assertions**.
+Teknik casting pada `any` disebut dengan **type assertions**.
## A.28.4. Casting Variabel Interface Kosong Ke Objek Pointer
@@ -115,7 +117,7 @@ Variabel `secret` dideklarasikan bertipe `interface{}` menampung referensi objek
## A.28.5. Kombinasi Slice, `map`, dan `interface{}`
-Tipe `[]map[string]interface{}` adalah salah satu tipe yang paling sering digunakan (menurut saya), karena tipe data tersebut bisa menjadi alternatif tipe slice struct.
+Tipe `[]map[string]interface{}` adalah salah satu tipe yang paling sering digunakan untuk menyimpan sekumpulan data berbasis *key-value*. Tipe tersebut merupakan alternatif dari slice struct.
Pada contoh berikut, variabel `person` dideklarasikan berisi data slice `map` berisikan 2 item dengan key adalah `name` dan `age`.
diff --git a/content/A-interface.md b/content/A-interface.md
index c8bb9a55e..136d76ed0 100644
--- a/content/A-interface.md
+++ b/content/A-interface.md
@@ -1,12 +1,12 @@
# A.27. Interface
-Interface adalah kumpulan definisi method yang tidak memiliki isi (hanya definisi saja), yang dibungkus dengan nama tertentu.
+Interface adalah definisi suatu kumpulan method yang tidak memiliki isi, jadi hanya definisi header/schema-nya saja. Kumpulan method tersebut ditulis dalam satu block interface dengan nama tertentu.
-Interface merupakan tipe data. Nilai objek bertipe interface zero value-nya adalah `nil`. Interface mulai bisa digunakan jika sudah ada isinya, yaitu objek konkret yang memiliki definisi method minimal sama dengan yang ada di interface-nya.
+Interface merupakan tipe data. Objek bertipe interface memiliki zero value yaitu `nil`. Variabel bertipe interface digunakan untuk menampung nilai objek konkret yang memiliki definisi method minimal sama dengan yang ada di interface.
## A.27.1. Penerapan Interface
-Yang pertama perlu dilakukan untuk menerapkan interface adalah menyiapkan interface beserta definisi method nya. Keyword `type` dan `interface` digunakan untuk pendefinisian interface.
+Untuk menerapkan interface, pertama siapkan deklarasi tipe baru menggunakan keyword `type` dan tipe data `interface` lalu siapkan juga isinya (definisi method-nya).
```go
package main
@@ -20,11 +20,11 @@ type hitung interface {
}
```
-Pada kode di atas, interface `hitung` memiliki 2 definisi method, `luas()` dan `keliling()`. Interface ini nantinya digunakan sebagai tipe data pada variabel, di mana variabel tersebut akan menampung objek bangun datar hasil dari struct yang akan kita buat.
+Di atas, interface `hitung` dideklarasikan memiliki 2 buah method yaitu `luas()` dan `keliling()`. Interface ini nantinya digunakan sebagai tipe data pada variabel untuk menampung objek bangun datar hasil dari struct yang akan dibuat.
-Dengan memanfaatkan interface `hitung`, perhitungan luas dan keliling bangun datar bisa dilakukan, tanpa perlu tahu jenis bangun datarnya sendiri itu apa.
+Dengan adanya interface `hitung` ini, maka perhitungan luas dan keliling bangun datar bisa dilakukan tanpa perlu tahu jenis bangun datarnya sendiri itu apa.
-Siapkan struct bangun datar `lingkaran`, struct ini memiliki method yang beberapa di antaranya terdefinisi di interface `hitung`.
+Selanjutnya, siapkan struct bangun datar `lingkaran`, struct ini memiliki definisi method yang sebagian adalah ada di interface `hitung`.
```go
type lingkaran struct {
@@ -44,9 +44,9 @@ func (l lingkaran) keliling() float64 {
}
```
-Struct `lingkaran` di atas memiliki tiga method, `jariJari()`, `luas()`, dan `keliling()`.
+Struct `lingkaran` memiliki tiga buah method yaitu `jariJari()`, `luas()`, dan `keliling()`.
-Selanjutnya, siapkan struct bangun datar `persegi`.
+Berikutnya, siapkan struct bangun datar `persegi` berikut:
```go
type persegi struct {
@@ -62,9 +62,9 @@ func (p persegi) keliling() float64 {
}
```
-Perbedaan struct `persegi` dengan `lingkaran` terletak pada method `jariJari()`. Struct `persegi` tidak memiliki method tersebut. Tetapi meski demikian, variabel objek hasil cetakan 2 struct ini akan tetap bisa ditampung oleh variabel cetakan interface `hitung`, karena dua method yang ter-definisi di interface tersebut juga ada pada struct `persegi` dan `lingkaran`, yaitu `luas()` dan `keliling()`.
+Perbedaan struct `persegi` dengan `lingkaran` terletak pada method `jariJari()`. Struct `persegi` tidak memiliki method tersebut. Tetapi meski demikian, variabel objek hasil cetakan 2 struct ini akan tetap bisa ditampung oleh variabel cetakan interface `hitung`, karena dua method yang ter-definisi di interface tersebut juga ada pada struct `persegi` dan `lingkaran`, yaitu method `luas()` dan `keliling()`.
-Buat implementasi perhitungan di `main`.
+Sekarang buat implementasi perhitungan di fungsi `main()`.
```go
func main() {
@@ -89,7 +89,7 @@ Dari variabel tersebut, method `luas()` dan `keliling()` diakses. Secara otomati
![Pemanfaatan interface](images/A_interface_1_interface.png)
-Method `jariJari()` pada struct `lingkaran` tidak akan bisa diakses karena tidak terdefinisi dalam interface `hitung`. Pengaksesannya dengan paksa akan menyebabkan error.
+Method `jariJari()` pada struct `lingkaran` tidak akan bisa diakses karena tidak terdefinisi dalam interface `hitung`. Pengaksesannya secara paksa menyebabkan error.
Untuk mengakses method yang tidak ter-definisi di interface, variabel-nya harus di-casting terlebih dahulu ke tipe asli variabel konkritnya (pada kasus ini tipenya `lingkaran`), setelahnya method akan bisa diakses.
@@ -102,11 +102,13 @@ var bangunLingkaran lingkaran = bangunDatar.(lingkaran)
bangunLingkaran.jariJari()
```
-Perlu diketahui juga, jika ada interface yang menampung objek konkrit di mana struct-nya tidak memiliki salah satu method yang terdefinisi di interface, error juga akan muncul. Intinya kembali ke aturan awal, variabel interface hanya bisa menampung objek yang minimal memiliki semua method yang terdefinisi di interface-nya.
+> Metode casting pada tipe data interface biasa disebut dengan **type assertion**
+
+Perlu diketahui juga, jika ada interface yang menampung objek konkrit yang mana struct-nya tidak memiliki salah satu method yang terdefinisi di interface, maka error akan muncul. Intinya kembali ke aturan awal, variabel interface hanya bisa menampung objek yang minimal memiliki semua method yang terdefinisi di interface tersebut.
## A.27.2. Embedded Interface
-Interface bisa di-embed ke interface lain, sama seperti struct. Cara penerapannya juga sama, cukup dengan menuliskan nama interface yang ingin di-embed ke dalam interface tujuan.
+Interface bisa di-embed ke interface lain, sama seperti struct. Cara penerapannya juga sama, cukup dengan menuliskan nama interface yang ingin di-embed ke dalam body interface tujuan.
Pada contoh berikut, disiapkan interface bernama `hitung2d` dan `hitung3d`. Kedua interface tersebut kemudian di-embed ke interface baru bernama `hitung`.
@@ -131,7 +133,7 @@ type hitung interface {
}
```
-Interface `hitung2d` berisikan method untuk kalkulasi luas dan keliling, sedang `hitung3d` berisikan method untuk mencari volume bidang. Kedua interface tersebut diturunkan di interface `hitung`, menjadikannya memiliki kemampuan untuk menghitung luas, keliling, dan volume.
+Interface `hitung2d` berisikan method untuk kalkulasi luas dan keliling, sedang `hitung3d` berisikan method untuk mencari volume bidang. Kedua interface tersebut embed ke interface `hitung`, menjadikannya memiliki kemampuan untuk mengakses method `luas()`, `keliling()`, dan `volume()`.
Next, siapkan struct baru bernama `kubus` yang memiliki method `luas()`, `keliling()`, dan `volume()`.
@@ -155,7 +157,7 @@ func (k *kubus) keliling() float64 {
Objek hasil cetakan struct `kubus` di atas, nantinya akan ditampung oleh objek cetakan interface `hitung` yang isinya merupakan gabungan interface `hitung2d` dan `hitung3d`.
-Terakhir, buat implementasi-nya di main.
+Terakhir, buat implementasi-nya di fungsi `main()`.
```go
func main() {
@@ -168,7 +170,7 @@ func main() {
}
```
-Bisa dilihat di kode di atas, lewat interface `hitung`, method `luas`, `keliling`, dan `volume` bisa di akses.
+Bisa dilihat di kode di atas, lewat interface `hitung`, method `luas()`, `keliling()`, dan `volume()` bisa di akses.
Pada chapter [A.23. Pointer](/A-pointer.html) dijelaskan bahwa method pointer bisa diakses lewat variabel objek biasa dan variabel objek pointer. Variabel objek yang dicetak menggunakan struct yang memiliki method pointer, jika ditampung ke dalam variabel interface, harus diambil referensi-nya terlebih dahulu. Contohnya bisa dilihat pada kode di atas `var bangunRuang hitung = &kubus{4}`.
diff --git a/content/A-json.md b/content/A-json.md
index 72ce09b14..d20c97510 100644
--- a/content/A-json.md
+++ b/content/A-json.md
@@ -1,10 +1,10 @@
# A.53. JSON Data
-**JSON** atau *Javascript Object Notation* adalah notasi standar yang umum digunakan untuk komunikasi data dalam web. JSON merupakan subset dari *javascript*.
+**JSON** atau *Javascript Object Notation* adalah notasi standar penulisan data yang umum digunakan untuk komunikasi antar aplikasi/service. JSON sendiri sebenarnya merupakan subset dari *javascript*.
Go menyediakan package `encoding/json` yang berisikan banyak fungsi untuk kebutuhan operasi json.
-Pada chapter ini, kita akan belajar cara untuk konverstri string yang berbentuk json menjadi objek Go, dan sebaliknya.
+Pada chapter ini, kita akan belajar cara untuk konverstri string yang ditulis dalam format json menjadi objek Go, dan sebaliknya.
## A.53.1. Decode JSON Ke Variabel Objek Struct
@@ -24,9 +24,9 @@ type User struct {
}
```
-Struct `User` ini nantinya digunakan untuk membuat variabel baru penampung hasil decode json string. Proses decode sendiri dilakukan lewat fungsi `json.Unmarshal()`, dengan json string tersebut dimasukan ke statement fungsi tersebut.
+Struct `User` ini nantinya digunakan untuk membuat variabel baru penampung hasil decode json string. Proses decode sendiri dilakukan lewat fungsi `json.Unmarshal()`, dalam penggunaannya data json string dimasukan sebagai argument pemanggilan fungsi.
-Silakan tulis kode berikut.
+Contoh praktiknya bisa dilihat di bawah ini.
```go
func main() {
@@ -46,19 +46,19 @@ func main() {
}
```
-Fungsi unmarshal hanya menerima data json dalam bentuk `[]byte`, maka dari itu data json string pada kode di atas di-casting terlebih dahulu ke tipe `[]byte` sebelum dipergunakan pada fungsi unmarshal.
+Fungsi unmarshal hanya menerima data json dalam bentuk `[]byte`, maka dari itu data json string perlu di-casting terlebih dahulu ke tipe `[]byte`, sebelum akhirnya digunakan pada pemanggilan fungsi `json.Unmarshal()`.
-Juga, perlu diperhatikan, argument ke-2 fungsi unmarshal harus diisi dengan **pointer** dari objek yang nantinya akan menampung hasilnya.
+Perlu diperhatikan, argument ke-2 pemanggilan fungsi tersebut harus diisi dengan variabel **pointer** yang nantinya akan menampung hasil operasi decoding.
![Decode data json ke variabel objek](images/A_json_1_decode.png)
-Jika kita perhatikan lagi, pada struct `User`, salah satu property-nya yaitu `FullName` memiliki **tag** `json:"Name"`. Tag tersebut digunakan untuk mapping informasi json ke property yang bersangkutan.
+Property `FullName` milik struct `User` memiliki **tag** `json:"Name"`. Tag tersebut digunakan untuk mapping informasi field json ke property struct.
-Data json yang akan diparsing memiliki 2 property yaitu `Name` dan `Age`. Kebetulan penulisan `Age` pada data json dan pada struktur struct adalah sama, berbeda dengan `Name` yang tidak ada pada struct.
+Data json yang akan di-parsing memiliki 2 property yaitu `Name` dan `Age`. Di contoh, penulisan `Age` di data json dan pada struktur struct adalah sama, berbeda dengan `Name` yang ada di data json tapi tidak ada di struct.
Dengan menambahkan tag json, maka property `FullName` struct akan secara cerdas menampung data json property `Name`.
-> Pada kasus decoding data json string ke variabel objek struct, semua level akses property struct penampung harus publik.
+> Pada operasi decoding data json string ke variabel objek struct, semua level akses property struct penampung harus publik.
## A.53.2. Decode JSON Ke `map[string]interface{}` & `interface{}`
@@ -85,7 +85,7 @@ fmt.Println("age :", decodedData["Age"])
## A.53.3. Decode Array JSON Ke Array Objek
-Decode data dari array json ke slice/array objek masih sama, siapkan saja variabel penampung hasil decode dengan tipe slice struct. Contohnya bisa dilihat pada kode berikut.
+Operasi decode data dari array json ke slice/array objek caranya juga sama. Langsung praktek saja agar lebih jelas. Siapkan sebuah variabel baru untuk menampung hasil decode dengan tipe slice struct, lalu gunakan pada fungsi `json.Unmarshal()`.
```go
var jsonString = `[
@@ -107,11 +107,11 @@ fmt.Println("user 2:", data[1].FullName)
## A.53.4. Encode Objek Ke JSON String
-Setelah sebelumnya dijelaskan beberapa cara decode data dari json string ke objek, sekarang kita akan belajar cara **encode** data objek ke bentuk json string.
+Setelah sebelumnya dijelaskan beberapa cara decode data dari json string ke objek, sekarang kita akan belajar cara **encode** data objek di Go ke bentuk json string.
-Fungsi `json.Marshal` digunakan untuk encoding data ke json string. Sumber data bisa berupa variabel objek cetakan struct, `map[string]interface{}`, atau slice.
+Fungsi `json.Marshal()` digunakan untuk encoding data ke json string. Sumber data bisa berupa variabel objek cetakan struct, data bertipe `map[string]interface{}`, slice, atau lainnya.
-Pada contoh berikut, data slice struct dikonversi ke dalam bentuk json string. Hasil konversi berupa `[]byte`, casting terlebih dahulu ke tipe `string` agar bisa ditampilkan bentuk json string-nya.
+Pada contoh berikut, data slice struct dikonversi ke dalam bentuk json string. Hasil konversi adalah data bertipe `[]byte`, maka pastikan untuk meng-casting terlebih dahulu ke tipe `string` agar bisa ditampilkan bentuk json string-nya.
```go
var object = []User{{"john wick", 27}, {"ethan hunt", 32}}
@@ -125,7 +125,7 @@ var jsonString = string(jsonData)
fmt.Println(jsonString)
```
-Output:
+Output program:
![Encode data ke JSON](images/A_json_2_encode.png)
diff --git a/content/A-komentar.md b/content/A-komentar.md
index ba737d4ba..0790e783b 100644
--- a/content/A-komentar.md
+++ b/content/A-komentar.md
@@ -1,6 +1,6 @@
# A.8. Komentar
-Komentar biasa dimanfaatkan untuk menyisipkan catatan pada kode program, menulis penjelasan/deskripsi mengenai suatu blok kode, atau bisa juga digunakan untuk me-*remark* kode (men-non-aktifkan kode yg tidak digunakan). Komentar akan diabaikan ketika kompilasi maupun eksekusi program.
+Komentar biasa dimanfaatkan untuk untuk menyisipkan catatan pada kode program, atau untuk menulis penjelasan/deskripsi mengenai suatu blok kode, atau bisa juga digunakan untuk me-*remark* kode (men-non-aktifkan kode yg tidak digunakan). Komentar selalu diabaikan ketika kompilasi maupun eksekusi program.
Ada 2 jenis komentar di Go, *inline* & *multiline*. Pada pembahasan ini akan dijelaskan tentang penerapan dan perbedaan kedua jenis komentar tersebut.
@@ -22,7 +22,7 @@ func main() {
}
```
-Mari kita praktekan kode di atas. Siapkan file program baru dalam project folder (bisa buat project baru atau gunakan project yang sudah ada). Kemudian isi dengan kode di atas, lalu jalankan.
+Mari kita praktekan kode di atas. Siapkan file program baru dalam project folder (bisa buat project baru atau gunakan project yang sudah ada). Kemudian isi file dengan kode di atas, lalu jalankan.
![Contoh komentar inline](images/A_komentar_1_inline_comment.png)
diff --git a/content/A-konstanta.md b/content/A-konstanta.md
index 52c3a60e5..e3c130222 100644
--- a/content/A-konstanta.md
+++ b/content/A-konstanta.md
@@ -1,30 +1,30 @@
# A.11. Konstanta
-Konstanta adalah jenis variabel yang nilainya tidak bisa diubah. Inisialisasi nilai hanya dilakukan sekali di awal, setelah itu variabel tidak bisa diubah nilainya.
+Konstanta adalah jenis variabel yang nilainya tidak bisa diubah setelah dideklarasikan. Inisialisasi nilai konstanta hanya dilakukan sekali saja di awal, setelah itu variabel tidak bisa diubah nilainya.
## A.11.1. Penggunaan Konstanta
-Data seperti **pi** (22/7), kecepatan cahaya (299.792.458 m/s), adalah contoh data yang tepat jika dideklarasikan sebagai konstanta daripada variabel, karena nilainya sudah pasti dan tidak berubah.
+Data seperti **pi** (22/7), kecepatan cahaya (299.792.458 m/s), adalah contoh data yang tepat untuk dideklarasikan sebagai konstanta (daripada variabel), karena nilainya sudah pasti dan tidak akan berubah.
-Cara penerapan konstanta sama seperti deklarasi variabel biasa, selebihnya tinggal ganti keyword `var` dengan `const`.
+Cara penerapan konstanta sama seperti deklarasi variabel biasa, perbedaannya ada pada keyword yang digunakan, yaitu `const` (bukan `var`).
```go
const firstName string = "john"
fmt.Print("halo ", firstName, "!\n")
```
-Teknik type inference bisa diterapkan pada konstanta, caranya yaitu cukup dengan menghilangkan tipe data pada saat deklarasi.
+Teknik type inference bisa diterapkan pada konstanta, caranya cukup dengan menghilangkan tipe data pada saat deklarasi.
```go
const lastName = "wick"
fmt.Print("nice to meet you ", lastName, "!\n")
```
-#### • Penggunaan Fungsi `fmt.Print()`
+#### ◉ Penggunaan Fungsi `fmt.Print()`
-Fungsi ini memiliki peran yang sama seperti fungsi `fmt.Println()`, pembedanya fungsi `fmt.Print()` tidak menghasilkan baris baru di akhir outputnya.
+Fungsi ini memiliki peran yang sama seperti fungsi `fmt.Println()`, perbedaannya fungsi `fmt.Print()` tidak menghasilkan baris baru di akhir output-nya.
-Perbedaan lainnya adalah, nilai pada parameter-parameter yang dimasukkan ke fungsi tersebut digabungkan tanpa pemisah. Tidak seperti pada fungsi `fmt.Println()` yang nilai paremeternya digabung menggunakan penghubung spasi.
+Perbedaan lainnya: nilai argument parameter yang ditulis saat pemanggilan fungsi akan di-print tanpa pemisah. Tidak seperti pada fungsi `fmt.Println()` yang nilai argument paremeternya dipisah menggunakan karakter spasi.
```go
fmt.Println("john wick")
@@ -37,13 +37,11 @@ fmt.Print("john", " ", "wick\n")
Kode di atas menunjukkan perbedaan antara `fmt.Println()` dan `fmt.Print()`. Output yang dihasilkan oleh 5 statement di atas adalah sama, meski cara yang digunakan berbeda.
-Bila menggunakan `fmt.Println()` tidak perlu menambahkan spasi di tiap kata, karena fungsi tersebut akan secara otomatis menambahkannya di sela-sela nilai. Berbeda dengan `fmt.Print()`, perlu ditambahkan spasi, karena fungsi ini tidak menambahkan spasi di sela-sela nilai parameter yang digabungkan.
+Bila menggunakan `fmt.Println()`, maka tidak perlu menambahkan spasi di tiap kata, karena fungsi tersebut akan secara otomatis menambahkannya di sela-sela text. Berbeda dengan `fmt.Print()` yang perlu ditambahkan spasi, karena fungsi ini tidak menambahkan spasi secara otomatis di sela-sela nilai text yang digabungkan.
## A.11.2. Deklarasi Multi Konstanta
-Sama seperti variabel, konstanta juga dapat dideklarasikan secara bersamaan.
-
-Berikut adalah contoh deklarasi konstanta dengan tipe data dan nilai yang berbeda.
+Sama seperti variabel, konstanta juga dapat dideklarasikan secara bersamaan. Berikut adalah contoh deklarasi konstanta dengan tipe data dan nilai yang berbeda.
```go
const (
diff --git a/content/A-map.md b/content/A-map.md
index 4991d3058..d8fa8aff8 100644
--- a/content/A-map.md
+++ b/content/A-map.md
@@ -1,12 +1,12 @@
# A.17. Map
-**Map** adalah tipe data asosiatif yang ada di Go, berbentuk *key-value pair*. Untuk setiap data (atau value) yang disimpan, disiapkan juga key-nya. Key harus unik, karena digunakan sebagai penanda (atau identifier) untuk pengaksesan value yang bersangkutan.
+**Map** adalah tipe data asosiatif yang ada di Go yang berbentuk *key-value pair*. Data/value yang disimpan di map selalu disertai dengan key. Key sendiri harus unik, karena digunakan sebagai penanda (atau identifier) untuk pengaksesan value yang disimpan di map.
-Kalau dilihat, `map` mirip seperti slice, hanya saja indeks yang digunakan untuk pengaksesan bisa ditentukan sendiri tipe-nya (indeks tersebut adalah key).
+Kalau dilihat, `map` mirip seperti slice, hanya saja identifier yang digunakan untuk pengaksesan bukanlah index numerik, melainkan bisa dalam tipe data apapun sesuai dengan yang diinginkan.
## A.17.1. Penggunaan Map
-Cara menggunakan map cukup dengan menuliskan keyword `map` diikuti tipe data key dan value-nya. Agar lebih mudah dipahami, silakan perhatikan contoh di bawah ini.
+Cara pengaplikasian map cukup mudah, dengan menuliskan keyword `map` diikuti tipe data key dan value-nya. Silakan perhatikan contoh di bawah ini agar lebih jelas.
```go
var chicken map[string]int
@@ -19,23 +19,25 @@ fmt.Println("januari", chicken["januari"]) // januari 50
fmt.Println("mei", chicken["mei"]) // mei 0
```
-Variabel `chicken` dideklarasikan sebagai map, dengan tipe data key adalah `string` dan value-nya `int`. Dari kode tersebut bisa dilihat bagaimana cara penggunaan keyword `map`.
+Variabel `chicken` dideklarasikan bertipe data map, dengan key ditentukan tipenya adalah `string` dan tipe value-nya `int`. Dari kode tersebut bisa dilihat bagaimana cara penerapan keyword `map` untuk pembuatan variabel.
-Kode `map[string]int` maknanya adalah, tipe data `map` dengan key bertipe `string` dan value bertipe `int`.
+Kode `map[string]int` merepresentasikan tipe data `map` dengan key bertipe `string` dan value bertipe `int`.
-Default nilai variabel `map` adalah `nil`. Oleh karena itu perlu dilakukan inisialisasi nilai default di awal, caranya cukup dengan tambahkan kurung kurawal pada akhir tipe, contoh seperti pada kode di atas: `map[string]int{}`.
+Zero value atau nilai default variabel `map` adalah `nil`. Dari sini maka penting untuk menginisialisasi nilai awal map agar tidak `nil`. Jika dibiarkan `nil`, ketika map digunakan untuk menampung data pasti memunculkan error.
-Cara menge-set nilai pada sebuah map adalah dengan menuliskan variabel-nya, kemudian disisipkan `key` pada kurung siku variabel (mirip seperti cara pengaksesan elemen slice), lalu isi nilainya. Contohnya seperti `chicken["februari"] = 40`. Sedangkan cara pengambilan value adalah cukup dengan menyisipkan `key` pada kurung siku variabel.
+Cara untuk inisialisasi map dengan menambahkan kurung kurawal buka tutup di akhir penulisan map, contoh: `map[string]int{}`.
-Pengisian data pada map bersifat **overwrite**, ketika variabel sudah memiliki item dengan key yang sama, maka value lama akan ditimpa dengan value baru.
+Cara menambahkan item pada map adalah dengan menuliskan variabel-nya, kemudian diikuti dengan `key` pada kurung siku variabel (mirip seperti cara pengaksesan elemen slice), lalu operator `=`, kemudian nilai/data yang ingin disimpan. Contohnya seperti `chicken["februari"] = 40`. Sedangkan cara mengakses item map dengan cukup dengan menuliskan nama variabel diikuti kurung siku dan `key`.
+
+Pengisian data pada map bersifat **overwrite**, artinya variabel sudah memiliki item dengan key yang sama, maka value item yang lama (dengan key sama) akan ditimpa dengan value baru.
![Pengaksesan data map](images/A_map_1_map_set_get.png)
-Pada pengaksesan item menggunakan key yang belum tersimpan di map, akan dikembalikan nilai default tipe data value-nya. Contohnya seperti pada kode di atas, `chicken["mei"]` menghasilkan nilai 0 (nilai default tipe `int`), karena belum ada item yang tersimpan menggunakan key `"mei"`.
+Pengaksesan item menggunakan key yang belum tersimpan di map, menghasilkan data berupa nilai default sesuai tipe data value. Contohnya kode `chicken["mei"]` menghasilkan nilai 0 (nilai default tipe `int`), hal ini karena variabel map `chicken` tidak memiliki item dengan key `"mei"`.
## A.17.2. Inisialisasi Nilai Map
-Zero value dari map adalah `nil`, maka tiap variabel bertipe map harus di-inisialisasi secara explisit nilai awalnya (agar tidak `nil`).
+Zero value dari map adalah `nil`. Disarankan untuk menginisialisasi secara explisit nilai awalnya agar tidak `nil`.
```go
var data map[string]int
@@ -47,7 +49,7 @@ data["one"] = 1
// tidak ada error
```
-Nilai variabel bertipe map bisa didefinisikan di awal, caranya dengan menambahkan kurung kurawal setelah tipe data, lalu menuliskan key dan value di dalamnya. Cara ini sekilas mirip dengan definisi nilai array/slice namun dalam bentuk key-value.
+Nilai variabel bertipe map bisa didefinisikan di awal, caranya dengan menambahkan kurung kurawal setelah tipe data, kemudian menuliskan key dan value di dalam kurung kurawal tersebut. Cara ini sekilas mirip dengan definisi nilai array/slice namun dalam bentuk key-value.
```go
// cara horizontal
@@ -70,11 +72,11 @@ var chicken4 = make(map[string]int)
var chicken5 = *new(map[string]int)
```
-Khusus inisialisasi data menggunakan keyword `new`, yang dihasilkan adalah data pointer. Untuk mengambil nilai aslinya bisa dengan menggunakan tanda asterisk (`*`). Topik pointer akan dibahas lebih detail ketika sudah masuk [A.23. Pointer](/A-pointer.html).
+Khusus inisialisasi data menggunakan keyword `new`, yang dihasilkan adalah data pointer. Untuk mengambil nilai aslinya bisa dengan menggunakan tanda asterisk (`*`). Topik pointer nantinya dibahas lebih detail pada chapter [A.23. Pointer](/A-pointer.html).
## A.17.3. Iterasi Item Map Menggunakan `for` - `range`
-Item variabel `map` bisa di iterasi menggunakan `for` - `range`. Cara penerapannya masih sama seperti pada slice, pembedanya data yang dikembalikan di tiap perulangan adalah key dan value, bukan indeks dan elemen. Contohnya bisa dilihat pada kode berikut.
+Item variabel `map` bisa di iterasi menggunakan `for` - `range`. Cara penerapannya masih sama seperti pada slice, dengan perbedaan pada map data yang dikembalikan di tiap perulangan adalah key dan value (bukan indeks dan elemen). Contohnya bisa dilihat pada kode berikut.
```go
var chicken = map[string]int{
@@ -93,7 +95,7 @@ for key, val := range chicken {
## A.17.4. Menghapus Item Map
-Fungsi `delete()` digunakan untuk menghapus item dengan key tertentu pada variabel map. Cara penggunaannya, dengan memasukan objek map dan key item yang ingin dihapus sebagai parameter.
+Fungsi `delete()` digunakan untuk menghapus item dengan key tertentu pada variabel map. Cara penggunaannya, dengan memasukan objek map dan key item yang ingin dihapus sebagai argument pemanggilan fungsi `delete()`.
```go
var chicken = map[string]int{"januari": 50, "februari": 40}
@@ -107,15 +109,15 @@ fmt.Println(len(chicken)) // 1
fmt.Println(chicken)
```
-Item yang memiliki key `"januari"` dalam variabel `chicken` akan dihapus.
+Operasi di atas membuat item dengan key `"januari"` dalam variabel map `chicken` dihapus.
![Hapus item Map](images/A_map_3_map_delete_item.png)
-Fungsi `len()` jika digunakan pada map akan mengembalikan jumlah item.
+Penggunaan fungsi `len()` pada map mengembalikan informasi jumlah item.
## A.17.5. Deteksi Keberadaan Item Dengan Key Tertentu
-Ada cara untuk mengetahui apakah dalam sebuah variabel map terdapat item dengan key tertentu atau tidak, yaitu dengan memanfaatkan 2 variabel sebagai penampung nilai kembalian pengaksesan item. Return value ke-2 ini adalah opsional, isinya nilai `bool` yang menunjukkan ada atau tidaknya item yang dicari.
+Ada cara untuk mengetahui apakah dalam variabel map terdapat item dengan key tertentu atau tidak, yaitu dengan memanfaatkan 2 variabel sebagai penampung nilai kembalian pengaksesan item. Return value ke-2 sifatnya opsional, boleh ditulis boleh juga tidak. Isinya nilai `bool`, jika berisi `true` menandakan bahwa item yang dicari ada di map, jika `false` maka tidak ada.
```go
var chicken = map[string]int{"januari": 50, "februari": 40}
@@ -130,11 +132,9 @@ if isExist {
## A.17.6. Kombinasi Slice & Map
-Slice dan `map` bisa dikombinasikan, dan sering digunakan pada banyak kasus, contohnya seperti data array yang berisikan informasi siswa, dan banyak lainnya.
-
-Cara menggunakannya cukup mudah, contohnya seperti `[]map[string]int`, artinya slice yang tipe tiap elemen-nya adalah `map[string]int`.
+Slice dan `map` bisa dikombinasikan, dan pada praktiknya cukup sering digunakan, contohnya untuk keperluan penyimpanan data array yang berisikan informasi siswa, dan banyak lainnya.
-Agar lebih jelas, silakan praktekan contoh berikut.
+Cara penerapannya cukup mudah, contohnya `[]map[string]int`, tipe tersebut artinya adalah sebuah slice yang tipe setiap elemen-nya adalah `map[string]int`. Agar lebih jelas, silakan praktekan contoh berikut.
```go
var chickens = []map[string]string{
@@ -148,9 +148,9 @@ for _, chicken := range chickens {
}
```
-Variabel `chickens` di atas berisikan informasi bertipe `map[string]string`, yang kebetulan tiap elemen memiliki 2 key yang sama.
+Variabel `chickens` di atas berisikan 3 buah item bertipe `map[string]string`. Ketiga item tersebut dideklarasikan memiliki 2 key yang sama, yaitu `name` dan `gender`.
-Jika anda menggunakan versi go terbaru, cara deklarasi slice-map bisa dipersingkat, tipe tiap elemen tidak wajib untuk dituliskan.
+Penulisan tipe data tiap item adalah opsional. Boleh ditulis atau tidak. Contoh alternatif penulisan:
```go
var chickens = []map[string]string{
@@ -160,7 +160,7 @@ var chickens = []map[string]string{
}
```
-Dalam `[]map[string]string`, tiap elemen bisa saja memiliki key yang berbeda-beda, sebagai contoh seperti kode berikut.
+Dalam `[]map[string]string`, tiap elemen bisa saja memiliki key yang berbeda-beda, contohnya seperti kode berikut.
```go
var data = []map[string]string{
diff --git a/content/A-method.md b/content/A-method.md
index 491676ade..ba0d3f47f 100644
--- a/content/A-method.md
+++ b/content/A-method.md
@@ -1,12 +1,14 @@
# A.25. Method
-**Method** adalah fungsi yang menempel pada `type` (bisa `struct` atau tipe data lainnya). Method bisa diakses lewat variabel objek.
+**Method** adalah fungsi yang menempel pada suatu tipe data, misalnya custom `struct`. Method bisa diakses lewat variabel objek yang dibuat dari tipe custom struct tersebut.
-Keunggulan method dibanding fungsi biasa adalah memiliki akses ke property struct hingga level *private* (level akses nantinya akan dibahas lebih detail pada chapter selanjutnya). Dan juga, dengan menggunakan method sebuah proses bisa di-enkapsulasi dengan baik.
+Keunggulan method dibanding fungsi biasa adalah method memiliki akses ke property struct hingga level akses *private*. Selain itu, dengan menggunakan method, suatu proses bisa di-enkapsulasi dengan baik.
+
+> Perihal topik level nantinya dibahas secara terpisah pada chapter berikutnya
## A.25.1. Penerapan Method
-Cara menerapkan method sedikit berbeda dibanding penggunaan fungsi. Ketika deklarasi, ditentukan juga siapa pemilik method tersebut. Contohnya bisa dilihat pada kode berikut:
+Cara penerapan method sedikit berbeda dibanding fungsi. Saat proses deklarasi, pada method perlu ditentukan juga siapa pemiliknya. Contohnya bisa dilihat pada kode berikut, dua method diciptakan sebagai property dari struct bernama `student`.
```go
package main
@@ -28,9 +30,9 @@ func (s student) getNameAt(i int) string {
}
```
-Cara deklarasi method sama seperti fungsi, hanya saja perlu ditambahkan deklarasi variabel objek di sela-sela keyword `func` dan nama fungsi. Struct yang digunakan akan menjadi pemilik method.
+Cara deklarasi method mirip seperti fungsi, tapi dalam penulisannya perlu ditambahkan deklarasi variabel objek di sela-sela keyword `func` dan nama fungsi. Pada contoh di atas struct `student` ditentukan sebagai pemilik method.
-`func (s student) sayHello()` maksudnya adalah fungsi `sayHello` dideklarasikan sebagai method milik struct `student`. Pada contoh di atas struct `student` memiliki dua buah method, yaitu `sayHello()` dan `getNameAt()`.
+`func (s student) sayHello()` maksudnya adalah fungsi `sayHello` dideklarasikan sebagai method milik struct `student`. Di contoh, struct `student` memiliki dua buah method yaitu `sayHello()` dan `getNameAt()`.
Contoh pemanfaatan method bisa dilihat pada kode berikut.
@@ -44,32 +46,34 @@ func main() {
}
```
-Output:
+Output program:
![Penggunaan method](images/A_method_1_method.png)
-Cara mengakses method sama seperti pengaksesan properti berupa variabel. Tinggal panggil saja methodnya.
+Cara mengakses method sama seperti pada pengaksesan property, yaitu dengan cukup panggil saja nama methodnya.
```go
s1.sayHello()
var name = s1.getNameAt(2)
```
-Method memiliki sifat yang sama persis dengan fungsi biasa. Seperti bisa berparameter, memiliki nilai balik, dan lainnya. Dari segi sintaks, pembedanya hanya ketika pengaksesan dan deklarasi. Bisa dilihat di kode berikut, sekilas perbandingan penulisan fungsi dan method.
+Method memiliki sifat yang sama persis dengan fungsi biasa, yaitu bisa memiliki parameter, nilai balik, dan sifat-sifat lainnya.
+
+Dari segi sintaks, perbedaan yang paling terlihat hanya di bagian penulisan deklarasi dan cara pengaksesan. Silakan lihat kode berikut agar lebih jelas:
```go
-func sayHello() {
-func (s student) sayHello() {
+func sayHello() { }
+func (s student) sayHello() { }
-func getNameAt(i int) string {
-func (s student) getNameAt(i int) string {
+func getNameAt(i int) string { }
+func (s student) getNameAt(i int) string { }
```
## A.25.2. Method Pointer
-Method pointer adalah method yang variabel objek pemilik method tersebut berupa pointer.
+Method pointer adalah method yang dimana variabel objek pemilik method tersebut adalah berbentuk pointer.
-Kelebihan method jenis ini adalah, ketika kita melakukan manipulasi nilai pada property lain yang masih satu struct, nilai pada property tersebut akan di rubah pada reference nya. Lebih jelasnya perhatikan kode berikut.
+Kelebihan method jenis ini adalah ketika kita melakukan manipulasi nilai pada property lain yang masih satu struct, nilai pada property tersebut bisa diubah di-level reference-nya. Lebih jelasnya perhatikan kode berikut.
```go
package main
@@ -106,13 +110,13 @@ func main() {
}
```
-Output:
+Output program:
![Penggunaan method pointer](images/A_method_2_method_pointer.png)
-Setelah eksekusi statement `s1.changeName1("jason bourne")`, nilai `s1.name` tidak berubah. Sebenarnya nilainya berubah tapi hanya dalam method `changeName1()` saja, nilai pada reference di objek-nya tidak berubah. Karena itulah ketika objek di print value dari `s1.name` tidak berubah.
+Setelah statement `s1.changeName1("jason bourne")` dieksekusi, nilai `s1.name` tidak berubah. Sebenarnya nilainya berubah tapi hanya dalam method `changeName1()` saja, nilai pada reference objek-nya tidak berubah.
-Keistimewaan lain method pointer adalah, method itu sendiri bisa dipanggil dari objek pointer maupun objek biasa.
+Keistimewaan lain method pointer adalah method itu sendiri bisa dipanggil dari objek pointer maupun objek biasa.
```go
// pengaksesan method dari variabel objek biasa
@@ -124,28 +128,31 @@ var s2 = &student{"ethan hunt", 22}
s2.sayHello()
```
----
+## A.25.3. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai beberapa hal pada chapter ini.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Penggunaan Fungsi `strings.Split()`
+#### ◉ Penggunaan Fungsi `strings.Split()`
-Pada chapter ini ada fungsi baru yang kita gunakan: `strings.Split()`. Fungsi ini berguna untuk memisah string menggunakan pemisah yang ditentukan sendiri. Hasilnya adalah array berisikan kumpulan substring.
+Pada chapter ini ada fungsi baru yang kita gunakan saat praktek, yaitu `strings.Split()`. Fungsi ini berguna untuk memisahkan string menggunakan pemisah yang kita tentukan sendiri. Hasilnya berupa array berisikan kumpulan substring.
```go
strings.Split("ethan hunt", " ")
// ["ethan", "hunt"]
```
-Pada contoh di atas, string `"ethan hunt"` dipisah menggunakan separator spasi `" "`. Maka hasilnya terbentuk array berisikan 2 data, `"ethan"` dan `"hunt"`.
+Pada contoh di atas, string `"ethan hunt"` dipisah menggunakan separator spasi `" "`, hasilnya adalah array berisikan 2 elemen, `"ethan"` dan `"hunt"`.
## A.25.3. Apakah `fmt.Println()` & `strings.Split()` Juga Merupakan Method?
-Setelah tahu apa itu method dan bagaimana penggunaannya, mungkin akan muncul di benak kita bahwa kode seperti `fmt.Println()`, `strings.Split()` dan lainnya-yang-berada-pada-package-lain adalah merupakan method. Tapi sayangnya **bukan**. `fmt` di situ bukanlah variabel objek, dan `Println()` bukan merupakan method-nya.
+Setelah tahu apa itu method dan bagaimana penggunaannya, mungkin akan muncul di benak kita bahwa kode seperti `fmt.Println()`, `strings.Split()` dan lainnya-yang-berada-pada-package-lain adalah merupakan method. Jawabannya,**bukan!**. `fmt` di situ bukanlah variabel objek, dan `Println()` bukan merupakan method.
+
+`fmt` adalah nama **package** yang di-import (bisa dilihat pada kode `import "fmt"`). Sedangkan `Println()` adalah **nama fungsi**. Untuk mengakses fungsi yang berada pada package lain, harus dituliskan juga nama package-nya, contoh:
-`fmt` adalah nama **package** yang di-import (bisa dilihat pada kode `import "fmt"`). Sedangkan `Println()` adalah **nama fungsi**. Untuk mengakses fungsi yang berada pada package lain, harus dituliskan nama package-nya. Hal ini berlaku juga di dalam package `main`. Jika ada fungsi dalam package main yang diakses dari package lain yang berbeda, maka penulisannya `main.NamaFungsi()`.
+- Statement `fmt.Println()` berarti pengaksesan fungsi `Println()` yang berada di package `fmt`
+- Statement `strings.Split()` berarti pengaksesan fungsi `Split()` yang berada di package `strings`
-Lebih detailnya akan dibahas pada chapter selanjutnya.
+Lebih detailnya dibahas pada chapter selanjutnya.
---
diff --git a/content/A-perulangan.md b/content/A-perulangan.md
index a3f66a746..bc0d43608 100644
--- a/content/A-perulangan.md
+++ b/content/A-perulangan.md
@@ -81,6 +81,11 @@ for k, v := range kvs {
for range kvs {
fmt.Println("Done")
}
+
+// selain itu, bisa juga dengan cukup menentukan nilai numerik perulangan
+for i := range 5 {
+ fmt.Print(i) // 01234
+}
```
## A.14.5. Penggunaan Keyword `break` & `continue`
diff --git a/content/A-pipeline-context-cancellation.md b/content/A-pipeline-context-cancellation.md
index 442227033..e904ca11e 100644
--- a/content/A-pipeline-context-cancellation.md
+++ b/content/A-pipeline-context-cancellation.md
@@ -19,7 +19,7 @@ Jadi kurang lebih akan ada dua result:
Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program versi *concurrent* tanpa *cancellation*. Bisa langsung copy-paste, atau tulis dari awal dengan mengikut tutorial ini secara keseluruhan. Untuk penjelasan detail program versi sekuensial silakan merujuk ke chapter sebelumnya saja, di sini kita tulis langsung agar bisa cepat dimulai bagian program konkuren.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
```go
package main
@@ -40,7 +40,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.61-pipeline-cancellation-context")
```
-#### • Definisi struct `FileInfo`
+#### ◉ Definisi struct `FileInfo`
```go
type FileInfo struct {
@@ -51,7 +51,7 @@ type FileInfo struct {
}
```
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -65,7 +65,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -82,7 +82,7 @@ func randomString(length int) string {
}
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -111,7 +111,7 @@ func generateFiles() {
log.Printf("%d/%d of total files created", counterSuccess, counterTotal)
}
```
-#### • Fungsi `generateFileIndexes()`
+#### ◉ Fungsi `generateFileIndexes()`
```go
func generateFileIndexes() <-chan FileInfo {
@@ -131,7 +131,7 @@ func generateFileIndexes() <-chan FileInfo {
}
```
-#### • Fungsi `createFiles()`
+#### ◉ Fungsi `createFiles()`
```go
func createFiles(chanIn <-chan FileInfo, numberOfWorkers int) <-chan FileInfo {
@@ -179,7 +179,7 @@ Hasil eksekusi program:
Ok, sekarang kita akan refactor kode tersebut, kita tambahkan mekanisme *cancellation* menggunakan `context.Context` API. Silakan duplikasi file program, lalu ikuti petunjuk berikut.
-#### • Import package `context`
+#### ◉ Import package `context`
Tambahkan package `context` dalam block import packages.
@@ -191,7 +191,7 @@ import (
)
```
-#### • Tambahkan definisi konstanta timeout
+#### ◉ Tambahkan definisi konstanta timeout
Di sini saya tentukan timeout adalah 3 detik. Nantinya kita akan modifikasi angka timeout untuk keperluan testing.
@@ -199,7 +199,7 @@ Di sini saya tentukan timeout adalah 3 detik. Nantinya kita akan modifikasi angk
const timeoutDuration = 3 * time.Second
```
-#### • Penerapan context di fungsi `main()`
+#### ◉ Penerapan context di fungsi `main()`
Pada fungsi main, lakukan sedikit perubahan. Yang sebelumnya ada statement berikut:
@@ -253,7 +253,7 @@ Jadi pada contoh yang kita tulis di atas, kurang lebih yang akan dilakukan adala
* Fungsi `generateFilesWithContext()` dipanggil dengan disisipkan object context.
* Callback `context.CancelFunc` dipanggil secara deferred. Ini merupakan idiomatic Go dalam penerapan context. Meskipun context sudah punya timeout atau deadline dan kita tidak perlu meng-*cancel* context secara manual, sangat dianjurkan untuk tetap memanggil callback `cancel()` tersebut secara deferred.
-#### • Modifikasi fungsi `generateFiles()`
+#### ◉ Modifikasi fungsi `generateFiles()`
Isi dari fungsi `generateFiles()` kita ubah menjadi pemanggilan fungsi `generateFilesWithContext()` dengan parameter context kosong.
@@ -319,7 +319,7 @@ Nah jadi lewat seleksi kondisi 2 case di atas, kita bisa dengan mudah mengidenti
Selain beberapa hal yang sudah saya sampaikan, ada *minor changes* lainnya, yaitu pada pemanggilan fungsi `generateFileIndexes()` dan `createFiles()` ditambahkan argument context.
-#### • Penambahan context pada fungsi `generateFiles()`
+#### ◉ Penambahan context pada fungsi `generateFiles()`
Kenapa ini perlu? karena **meski eksekusi fungsi `generateFilesWithContext()` otomatis di stop ketika cancelled, proses di dalamnya akan tetap berjalan jika tidak di-*handle* dengan baik *cancellation*-nya.**
@@ -355,7 +355,7 @@ Dibanding sebelumnya, perbedaannya adalah ada *channel selection*. Jadi di bagia
* Jika ada notif cancel paksa, maka case pertama akan terpenuhi, dan perulangan di-`break`.
* Selebihnya, pengiriman jobs akan berlangsung seperti normalnya.
-#### • Penambahan context pada fungsi `createFiles()`
+#### ◉ Penambahan context pada fungsi `createFiles()`
Hal yang sama (cancel di level sub prosees) juga perlu diterapkan pada `createFiles()`, karena jika tidak, maka proses pembuatan file akan tetap berjalan sesuai dengan jumlah jobs yang dikirim meskipun sudah di-cancel secara paksa.
diff --git a/content/A-properti-public-dan-private.md b/content/A-properti-public-dan-private.md
index 20c6a62f7..9382caad9 100644
--- a/content/A-properti-public-dan-private.md
+++ b/content/A-properti-public-dan-private.md
@@ -1,49 +1,51 @@
# A.26. Properti Public dan Private (Exported vs Unexported)
-Chapter ini membahas mengenai *property modifier* public dan private dalam Go. Kapan sebuah struct, fungsi, atau method bisa diakses dari package lain dan kapan tidak.
+Chapter ini membahas tentang *property modifier* public dan private yang ada di pemrograman Go. Peran dari *property modifier* adalah sebagai penentu kapan suatu struct, fungsi, atau method bisa diakses dari package lain dan kapan tidak.
-Di Go sebenarnya tidak ada istilah *public modifier* dan *private modifier*. Yang ada adalah **exported** yang kalau di bahasa lain ekuivalen dengan *public modifier*, dan **unexported** untuk *private modifier*.
+Di Go sebenarnya tidak ada istilah *public modifier* dan *private modifier*. Yang ada adalah **exported** (yang kalau di bahasa lain ekuivalen dengan *public modifier*), dan **unexported** untuk *private modifier*.
## A.26.1. Intro
-Intro ini ditulis agar pembaca tau ekspektasi yang penulis kejar pada chapter ini.
+Intro ini ditulis agar pembaca tau ekspektasi chapter ini sebenarnya apa.
-Pembahasan kali ini memiliki beberapa perbedaan dibanding lainnya. Jika pembaca mengikuti secara berurutan, membaca penjelasan dan pembahasan yang sudah tertulis, maka **pasti akan mendapati 3 buah error**. Di tiap-tiap error, sebenarnya sudah terlampir:
+Pembahasan kali ini memiliki beberapa perbedaan dibanding chapter lainnya. Jika pembaca mengikuti pembelajaran di chapter ini secara berurutan, dan benar-benar membaca penjelasan serta pembahasan yang sudah tertulis, maka nantinya **pasti menemui 3 buah error**.
+
+Di setiap error tersebut, sebenarnya sudah terlampir informasi berikut:
1. Screenshot error
-2. Penjelasan penyebab error
-3. Cara resolve atau solusi dari ketiga error tersebut.
+2. Penjelasan penyebab terjadinya error
+3. Cara resolve atau mengatasi error
+
+Penulis menerima cukup banyak email dari pembaca mengenai beberapa error di chapter ini. Kesimpulan penulis:
-Kesimpulan dari email-email yang penulis dapati: **pembaca bingung karena mendapati error, dan tidak tau cara mengatasi error tersebut. Padahal sudah ada keterangan yang jelas bahwa error tersebut pasti muncul, dan juga sudah dijelaskan cara mengatasinya. Ini kemungkinan besar disebabkan karena pembaca hanya copy-paste source code, tanpa membaca penjelasan-penjelasan yang padahal sudah tertulis cukup mendetail**.
+**Pembaca bingung karena mendapati error, dan tidak tau apa yang harus dilakukan. Padahal sudah ada keterangan yang cukup jelas bahwa error tersebut pasti muncul, dan sudah disediakan juga penjelasan beserta cara mengatasinya. Ini kemungkinan besar disebabkan karena pembaca hanya copy-paste source code dari chapter ini, tanpa benar-benar membaca penjelasan yang padahal sudah ditulis cukup detail**.
-> Saya sangat anjurkan, untuk itu saya mohon **jangan hanya *copas* source code, usahakan dibaca! dipelajari! dan dipahami!** *No hard feeling* ya 👌😁
+> Saya sangat anjurkan untuk **tidak hanya *copas* source code, usahakan dibaca! dipelajari! dan dipahami!** *No hard feeling* ya 👌😁
## A.26.2. Exported Package dan Unexported Package
-Pengembangan aplikasi dalam *real development* pasti membutuhkan banyak sekali file program. Tidak mungkin dalam sebuah project semua file memiliki nama package `main`, biasanya akan dipisah sebagai package berbeda sesuai bagiannya.
+Pengembangan aplikasi dalam *real development* pasti membutuhkan banyak sekali file program. Tidak mungkin dalam satu buah project semua source code di tulis di hanya 1 package `main` saja, umumnya akan dipisah ke beberapa package berbeda yang masing-masing punya tugas sendiri yang berbeda satu sama lain.
Project folder selain berisikan file-file `.go` juga bisa berisikan sub-folder lainnya. Di Go, setiap folder atau sub-folder adalah satu package, file-file yang ada di dalam sebuah folder package-nya harus sama. Dan package pada file-file tersebut harus berbeda dengan package pada file-file lainnya yang berada pada folder berbeda.
-> Jadi mudahnya, 1 folder adalah 1 package.
-
-Dalam sebuah package, biasanya kita menulis sangat banyak komponen, entah itu fungsi, struct, variabel, atau lainnya. Komponen tersebut bisa leluasa digunakan dalam package yang sama. Contoh sederhananya seperti program yang telah kita praktekan pada chapter sebelum-sebelumnya, dalam package `main` ada banyak yang di-*define*: fungsi, variabel, closure, struct, dan lainnya; ke semuanya bisa langsung dimanfaatkan.
+> Sederhananya, 1 folder adalah 1 package.
-Jika dalam satu program terdapat lebih dari 1 package, atau ada package lain selain `main`, maka komponen dalam package lain tersebut tidak bisa diakses secara bebas dari file yang package-nya `main`, karena tiap komponen memiliki hak akses.
+Dalam sebuah package, biasanya kita menulis sangat banyak komponen, bisa berupa fungsi, struct, variabel, atau lainnya. Komponen-komponen tersebut bisa secara leluasa dipergunakan di kode yang masih berada di dalam package yang sama. Contohnya seperti program yang telah kita praktekan pada chapter sebelum-sebelumnya, dalam package `main` ada banyak yang di-*define*: fungsi, variabel, closure, struct, dan lainnya; semuanya bisa langsung dimanfaatkan.
-Ada 2 jenis hak akses di Go:
+Jika dalam satu program terdapat lebih dari 1 package, atau ada package lain selain `main`, maka komponen dalam package lain tersebut tidak bisa diakses secara bebas dari file yang package-nya `main`, perlu dilihat dulu level akses yang sudah ditentukan apa.
- - Hak akses **Exported** atau **public**. Menandakan komponen tersebut diperbolehkan untuk diakses dari package lain yang berbeda
- - Hak akses **Unexported** atau **private**. Berarti komponen hanya bisa diakses dalam package yang sama, bisa dalam satu file saja atau dalam beberapa file yang masih 1 folder.
+Go mengenal 2 jenis level akses atau hak akses:
-Penentuan hak akses yang tepat untuk tiap komponen sangatlah penting.
+ - Hak akses **Exported** atau **public**. Menandakan bahwa komponen boleh untuk diakses dari package lain
+ - Hak akses **Unexported** atau **private**. Berarti komponen hanya bisa diakses dari file yang package-nya sama, bisa dalam satu file yang sama atau di file berbeda yang masih 1 folder yang package-nya pastinya sama.
-Di Go cara menentukan level akses atau modifier sangat mudah, penandanya adalah **character case** huruf pertama nama fungsi, struct, variabel, atau lainnya. Ketika namanya diawali dengan huruf kapital menandakan kalau *exported* (atau *public*). Dan sebaliknya, jika diawali huruf kecil, berarti *unexported* (atau private).
+Cara menentukan level akses atau modifier di Go sangat mudah, yaitu dengan mengacu ke **character case** huruf pertama nama fungsi, struct, variabel, atau lainnya. Ketika namanya diawali dengan huruf kapital maka level aksesnya adalah *exported* (atau *public*). Dan sebaliknya, jika diawali huruf kecil, berarti *unexported* (atau private).
## A.26.3. Penggunaan Package, Import, Dan Hak Akses *Exported* dan *Unexported*
Agar lebih mudah dipahami, maka langsung saja kita praktekan.
-Pertama buat folder proyek baru bernama `belajar-golang-level-akses`, inisialisasi sebagai project dengan nama yang sama. Kemudian buat file baru bernama `main.go` di dalamnya, lalu set nama package file tersebut sebagai **main**.
+Pertama buat folder proyek baru bernama `belajar-golang-level-akses`, gunakan nama folder tersebut sebagai nama project. Kemudian buat file baru bernama `main.go` di dalamnya, lalu tentukan nama package file tersebut sebagai **main**.
Kemudian, buat sub-folder baru bernama `library` di dalam folder `belajar-golang-level-akses`. Di dalam folder `library`, buat file baru `library.go`, set nama package-nya **library**.
@@ -65,12 +67,12 @@ func introduce(name string) {
}
```
-File `library.go` yang telah dibuat ditentukan nama package-nya adalah `library` (sesuai dengan nama folder), berisi dua buah fungsi, `SayHello()` dan `introduce()`.
+File `library.go` yang telah dibuat ditentukan nama package-nya adalah `library` (sesuai dengan nama folder), isinya dua buah fungsi `SayHello()` dan `introduce()`.
- Fungsi `SayHello()`, level aksesnya adalah publik, ditandai dengan nama fungsi diawali huruf besar.
- Fungsi `introduce()` dengan level akses private, ditandai oleh huruf kecil di awal nama fungsi.
-Selanjutnya kita lakukan tes apakah memang fungsi yang ber-modifier private dalam package `library` tidak bisa diakses dari package lain.
+Selanjutnya kita siapkan beberapa kode tambahan untuk keperluan testing apakah memang fungsi yang ber-modifier private dalam package `library` tidak bisa diakses dari package lain.
Buka file `main.go`, lalu tulis kode berikut.
@@ -87,26 +89,26 @@ func main() {
Bisa dilihat bahwa package `library` yang telah dibuat tadi, di-import ke dalam package `main`.
-Folder utama atau root folder dalam project yang sedang digarap adalah `belajar-golang-level-akses`, sehingga untuk import package lain yang merupakan subfolder, harus dituliskan lengkap path folder nya, seperti `belajar-golang-level-akses/library`.
+Di awal telah ditentukan bahwa nama project (yang juga merupakan nama folder) adalah `belajar-golang-level-akses`, maka untuk import package lain yang merupakan subfolder, pada syntax import harus dituliskan lengkap, contoh: `belajar-golang-level-akses/library`.
-Penanda root folder adalah tempat di mana file `go.mod` berada.
+> Penanda root folder adalah tempat di mana file `go.mod` berada
-Ok, kita lanjut. Perhatikan kode berikut.
+Kembali ke pembahasan kode, silakan perhatikan kode berikut:
```go
library.SayHello()
library.introduce("ethan")
```
-Cara pemanggilan fungsi yang berada dalam package lain adalah dengan menuliskan nama package target diikut dengan nama fungsi menggunakan *dot notation* atau tanda titik, seperti `library.SayHello()` atau `library.introduce("ethan")`
+Cara pemanggilan fungsi yang berada dalam package lain adalah dengan menuliskan nama package target diikut dengan nama fungsi menggunakan *dot notation* atau tanda titik, seperti `library.SayHello()` atau `library.introduce("ethan")`.
OK, sekarang coba jalankan kode yang sudah disiapkan di atas, hasilnya error.
![Error saat menjalankan program](images/A_properti_public_private_2_error.png)
-Error di atas disebabkan karena fungsi `introduce()` yang berada dalam package `library` memiliki level akses *unexported* (atau *private*), fungsi ini tidak bisa diakses dari package lain (pada kasus ini `main`). Agar bisa diakses, solusinya bisa dengan menjadikannya ke bentuk *exported* (atau *public*), atau diubah cara pemanggilannya. Di sini kita menggunakan cara ke-2.
+Error di atas disebabkan oleh fungsi `introduce()` yang berada dalam package `library` memiliki level akses *unexported* (atau *private*), maka fungsi ini tidak bisa diakses dari package lain (pada kasus ini package `main`). Solusi agar bisa diakses adalah dengan mengubah level aksesnya ke *exported* (atau *public*), atau bisa dengan mengubah cara pemanggilannya.
-Tambahkan parameter `name` pada fungsi `SayHello()`, lalu panggil fungsi `introduce()` dengan menyisipkan parameter `name` dari dalam fungsi `SayHello()`.
+Ok, sekarang kita akan coba cara ke-2, yaitu mengubah cara pemanggilannya. Tambahkan parameter `name` pada fungsi `SayHello()`, lalu masih di dalam fungsi tersebut panggil fungsi `introduce()` dan gunakan parameter `name`-nya.
```go
func SayHello(name string) {
@@ -115,7 +117,7 @@ func SayHello(name string) {
}
```
-Di `main`, cukup panggil fungsi `SayHello()` saja, sisipkan sebuah string sebagai parameter.
+Di fungsi `main()`, cukup panggil fungsi `library.SayHello()` saja. Isi parameternya dengan nilai string apapun, misalnya `"ethan"`.
```go
func main() {
@@ -131,7 +133,7 @@ Coba jalankan lagi.
Level akses *exported* (atau public) dan *unexported* (atau private) juga bisa diterapkan di fungsi, struct, method, maupun property variabel. Cara penggunaannya sama seperti pada pembahasan sebelumnya, yaitu dengan menentukan **character case** huruf pertama nama komponen, apakah huruf besar atau kecil.
-Belajar tentang level akses di Go akan lebih cepat jika langsung praktek. Oleh karena itu langsung saja. Hapus isi file `library.go`, buat struct baru dengan nama `student` di dalamnya.
+Ok, lanjut ke praktek berikutnya. Hapus isi file `library.go`, lalu buat struct baru dengan nama `student` di dalamnya.
```go
package library
@@ -161,26 +163,26 @@ Setelah itu jalankan program.
![Error saat menjalankan program](images/A_properti_public_private_3_error.png)
-Error muncul lagi, kali ini penyebabnya adalah karena struct `student` masih di set sebagai *unexported*. Ganti ke bentuk *exported* dengan cara mengubah huruf awalnya menjadi huruf besar, kemudian jalankan ulang.
+Error muncul lagi, kali ini penyebabnya adalah karena struct `student` level aksesnya adalah *unexported*. Ubah ke bentuk *exported* dengan cara mengubah huruf awalnya menjadi huruf besar, kemudian jalankan ulang.
```go
-// pada library/library.go
+// file library/library.go
type Student struct {
Name string
grade int
}
-// pada main.go
+// file main.go
var s1 = library.Student{"ethan", 21}
fmt.Println("name ", s1.Name)
fmt.Println("grade", s1.grade)
```
-Output:
+Output program:
![Error lain muncul saat menjalankan program](images/A_properti_public_private_4_error.png)
-Error masih tetap muncul, tapi kali ini berbeda. Error yang baru ini disebabkan karena salah satu properti dari struct `Student` adalah *unexported*. Properti yg dimaksud adalah `grade`. Ubah ke bentuk *exported*, lalu jalankan lagi.
+Error masih tetap muncul, tapi kali ini berbeda. Error yang baru ini disebabkan karena salah satu properti dari struct `Student` adalah *unexported*. Properti yg dimaksud adalah `grade`. Solusinya ubah ke bentuk *exported*, lalu jalankan ulang program.
```go
// pada library/library.go
@@ -201,11 +203,10 @@ Dari contoh program di atas, bisa disimpulkan bahwa untuk menggunakan `struct` y
## A.26.5. Import Dengan Prefix Tanda Titik
-> PERINGATAN! Penggunaan tanda titik pada saat import package bisa menyebabkan kode menjadi ambigu, karena alasan tersebut teknik import ini kurang direkomendasikan.
-
Seperti yang kita tahu, untuk mengakses fungsi/struct/variabel yg berada di package lain, nama package nya perlu ditulis, contohnya seperti pada penggunaan `library.Student` dan `fmt.Println()`.
-Di Go, komponen yang berada di package lain yang di-import bisa dijadikan se-level dengan komponen package peng-import, caranya dengan menambahkan tanda titik (`.`) setelah penulisan keyword `import`. Maksud dari se-level di sini adalah, semua properti di package lain yg di-import bisa diakses tanpa perlu menuliskan nama package, seperti ketika mengakses sesuatu dari file yang sama.
+Di Go, komponen yang berada di package lain yang di-import bisa dijadikan se-level dengan komponen package peng-import, caranya dengan menambahkan tanda titik (`.`) setelah penulisan keyword `import`. Maksud dari se-level di sini adalah, semua property di package lain yg di-import bisa diakses tanpa perlu menuliskan nama package, seolah-olah property tersebut berada di file yang sama. Contoh:
+
```go
import (
. "belajar-golang-level-akses/library"
@@ -221,9 +222,13 @@ func main() {
Pada kode di atas package `library` di-import menggunakan tanda titik. Dengan itu, pemanggilan struct `Student` tidak perlu dengan menuliskan nama package nya.
-## A.26.6. Pemanfaatan Alias Ketika Import Package
+> PERINGATAN!
+>
+> Penggunaan tanda titik pada saat import package bisa menyebabkan kode menjadi ambigu, karena alasan tersebut teknik import ini kurang direkomendasikan.
+
+## A.26.6. Pemanfaatan Alias Saat Import Package
-Fungsi yang berada di package lain bisa diakses dengan cara menuliskan nama-package diikuti nama fungsi-nya, contohnya seperti `fmt.Println()`. Package yang sudah di-import tersebut bisa diubah namanya dengan cara menggunakan alias pada saat import. Contohnya bisa dilihat pada kode berikut.
+Fungsi yang berada di package lain bisa diakses dengan cara menuliskan nama-package diikuti nama fungsi-nya, contohnya seperti `fmt.Println()`. Package yang sudah di-import tersebut bisa diubah nama pemanggilannya dengan menerapkan teknik alias yang dituliskan saat import. Contohnya bisa dilihat pada kode berikut.
```go
import (
@@ -237,15 +242,15 @@ func main() {
Pada kode di-atas, package `fmt` di tentukan aliasnya adalah `f`, untuk mengakses `Println()` cukup dengan `f.Println()`.
-## A.26.7. Mengakses Properti Dalam File Yang Package-nya Sama
+## A.26.7. Mengakses Property Dalam File Yang Package-nya Sama
-Jika properti yang ingin di akses masih dalam satu package tapi berbeda file, cara mengaksesnya bisa langsung dengan memanggil namanya. Hanya saja ketika eksekusi, file-file lain yang yang nama package-nya sama juga ikut dipanggil.
+Jika property yang ingin di akses masih dalam satu package tapi file-nya berbeda, cara mengaksesnya bisa langsung dengan memanggil namanya seperti biasa. Hanya saja saat eksekusi, file-file lain yang yang nama package-nya sama tersebut harus ikut disertakan dalam command `go run`.
-Langsung saja kita praktekan, buat file baru dalam `belajar-golang-level-akses` dengan nama `partial.go`.
+Langsung saja kita praktekan, buat file baru dalam folder `belajar-golang-level-akses` dengan nama `partial.go`.
![File `partial.go` disiapkan setara dengan file `main.go`](images/A_properti_public_private_5_structure.png)
-Tulis kode berikut pada file `partial.go`. File ini kita set package-nya `main` (sama dengan nama package file `main.go`).
+Tulis kode berikut pada file `partial.go`. File tersebut kita tentukan nama package-nya adalah `main` (sama dengan nama package file `main.go`).
```go
package main
@@ -257,7 +262,7 @@ func sayHello(name string) {
}
```
-Hapus semua isi file `main.go`, lalu silakan tulis kode berikut.
+Hapus semua isi file `main.go`, ganti dengan kode berikut.
```go
package main
@@ -273,17 +278,19 @@ Sekarang terdapat 2 file berbeda (`main.go` dan `partial.go`) dengan package ada
go run main.go partial.go
```
-Fungsi `sayHello` pada file `partial.go` bisa dikenali meski level aksesnya adalah *unexported*. Hal ini karena kedua file tersebut (`main.go` dan `partial.go`) memiliki package yang sama.
+Fungsi `sayHello` pada file `partial.go` bisa dikenali meski level aksesnya adalah *unexported*. Hal ini karena kedua file tersebut (`main.go` dan `partial.go`) memiliki nama package yang sama.
-> Cara lain untuk menjalankan program bisa dengan perintah `go run *.go`, dengan cara ini tidak perlu menuliskan nama file nya satu per satu.
+> Alternatif yang lebih praktis untuk menjalankan program bisa dengan perintah `go run *.go`, dengan cara ini maka tidak perlu menuliskan nama file-nya satu per satu.
![Pemanggilan fungsi unexported dari dalam package yang sama](images/A_properti_public_private_6_multi_main.png)
-## A.26.7.1. Fungsi `init()`
+## A.26.8. Penjelasan Tambahan
+
+#### ◉ Fungsi `init()`
-Selain fungsi `main()`, terdapat juga fungsi spesial, yaitu `init()`. Fungsi ini otomatis dipanggil pertama kali ketika aplikasi di-run. Jika fungsi ini berada dalam package main, maka dipanggil lebih dulu sebelum fungsi `main()` dieksekusi.
+Selain fungsi `main()`, terdapat juga fungsi spesial yaitu `init()`. Fungsi ini otomatis dipanggil saat pertama kali program dijalankan. Jika fungsi ini ditulis di package-package lain yang di-import di `main`, maka semua fungsi `init()` tersebut dipanggil lebih dulu sebelum fungsi `main()`.
-Langsung saja kita praktekkan. Buka file `library.go`, hapus isinya lalu isi dengan kode berikut.
+Agar lebih jelas mari praktekan. Buka file `library.go`, hapus isinya lalu isi dengan kode berikut.
```go
package library
@@ -319,14 +326,14 @@ func main() {
}
```
-Package `library` di-import, dan variabel `Student` dikonsumsi. Pada saat import package, fungsi `init()` yang berada di dalamnya langsung dieksekusi.
+Package `library` di-import, dan variabel `Student` dikonsumsi pada fungsi `main()`. Sewaktu package di-import, fungsi `init()` yang berada di dalamnya langsung dieksekusi.
-Property variabel objek `Student` akan diisi dan sebuah pesan ditampilkan ke console.
-
-Dalam sebuah package diperbolehkan ada banyak fungsi `init()` (urutan eksekusinya adalah sesuai file mana yg terlebih dahulu digunakan). Fungsi ini dipanggil sebelum fungsi `main()`, pada saat eksekusi program.
+Di dalam fungsi `init()`, property variabel objek `Student` diisi dan sebuah pesan ditampilkan ke console.
![Fungsi `init()`](images/A_properti_public_private_7_init.png)
+Di Go, setiap package masing-masing boleh memiliki fungsi `init()`. Fungsi tersebut hanya akan dieksekusi ketika package di-import dengan urutan eksekusinya adalah sesuai dengan package mana yg di-import terlebih dahulu. Dan kesemua fungsi `init()` dipanggil sebelum fungsi `main()`.
+
---
diff --git a/content/A-random.md b/content/A-random.md
index c2ee6666e..474293c6a 100644
--- a/content/A-random.md
+++ b/content/A-random.md
@@ -1,27 +1,27 @@
# A.39. Random
-Pada chapter ini kita akan belajar cara untuk mengutilisasi package `math/rand` untuk menciptakan data acak atau random.
+Pada chapter ini kita akan belajar pemanfaatan package `math/rand` untuk pembuatan data acak atau random.
## A.39.1. Definisi
Random Number Generator (RNG) merupakan sebuah perangkat (bisa software, bisa hardware) yang menghasilkan data deret/urutan angka yang sifatnya acak.
-RNG bisa berupa hardware yang murni bisa menghasilkan data angka acak, atau bisa saja sebuah [pseudo-random](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) yang menghasilkan deret angka-angka yang **terlihat acak** tetapi sebenarnya tidak benar-benar acak, yang deret angka tersebut sebenarnya merupakan hasil kalkulasi algoritma deterministik dan probabilitas. Jadi untuk pseudo-random ini, asalkan kita tau *state*-nya maka kita akan bisa menebak hasil deret angka random-nya.
+RNG bisa berupa hardware yang murni bisa menghasilkan data angka acak, atau bisa saja sebuah [pseudo-random](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) yang menghasilkan deret angka-angka yang **terlihat acak** tetapi sebenarnya tidak benar-benar acak. Deret angka tersebut sebenarnya merupakan hasil kalkulasi algoritma deterministik dan probabilitas. Jadi untuk pseudo-random ini, asalkan kita tau *state*-nya maka kita akan bisa menebak hasil deret angka random-nya.
-Dalam per-randoman-duniawi terdapat istilah **seed** atau titik mulai (*starting point*). Seed ini digunakan oleh RNG dalam peng-generate-an angka random di tiap urutannya.
+Dalam per-randoman-duniawi terdapat istilah **seed** atau titik mulai (*starting point*). Seed ini digunakan oleh RNG untuk pembuatan angka random.
Sedikit ilustrasi mengenai korelasi antara seed dengan RNG, agar lebih jelas.
-- Dimisalkan saya menggunakan seed yaitu angka `10`, maka ketika fungsi RNG dijalankan untuk pertama kalinya, output angka yang dihasilkan pasti `5221277731205826435`. Angka random tersebut pasti *fix* dan akan selalu menjadi output angka random pertama yang dihasilkan, ketika seed yang digunakan adalah angka `10`.
+- Dimisalkan saya menggunakan seed yaitu angka `10`, maka ketika fungsi RNG dijalankan untuk pertama kalinya, output angka yang dihasilkan pasti `5221277731205826435`. Angka random tersebut pasti *fix* dan akan selalu menjadi hasil pertama ketika seed yang digunakan adalah angka `10`.
- Misalnya lagi, fungsi RNG di-eksekusi untuk ke-dua kalinya, maka angka random kedua yang dihasilkan adalah pasti `3852159813000522384`. Dan seterusnya.
- Misalkan lagi, fungsi RNG di-eksekusi lagi, maka angka random ketiga pasti `8532807521486154107`.
- Jadi untuk seed angka `10`, akan selalu menghasilkan angka random ke-1: `5221277731205826435`, ke-2: `3852159813000522384`, ke-3 `8532807521486154107`. Meskipun fungsi random dijalankan di program yang berbeda, di waktu yang berbeda, di environment yang berbeda, jika seed adalah `10` maka deret angka random yang dihasilkan pasti sama seperti contoh di atas.
## A.39.2. Package `math/rand`
-Di Go terdapat sebuah package yaitu `math/rand` yang isinya banyak sekali API untuk keperluan penciptaan angka random. Package ini mengadopsi **PRNG** atau *pseudo-random* number generator. Deret angka random yang dihasilkan sangat tergantung dengan angka **seed** yang digunakan.
+Go menyediakan package `math/rand`, isinya banyak sekali API untuk keperluan pembuatan angka random. Package ini mengadopsi **PRNG** atau *pseudo-random* number generator. Deret angka random yang dihasilkan sangat tergantung dengan angka **seed** yang digunakan.
-Cara menggunakan package ini sangat mudah, yaitu cukup import `math/rand`, lalu set seed-nya, lalu panggil fungsi untuk generate angka random-nya. Lebih jelasnya silakan cek contoh berikut.
+Cara penggunaan package ini sangat mudah, cukup import `math/rand`, lalu tentukan nilai seed, kemudian panggil fungsi untuk generate angka random-nya. Lebih jelasnya silakan cek contoh berikut.
```go
package main
@@ -53,9 +53,9 @@ Jika perlu jalankan program di atas beberapa kali, hasilnya selalu sama untuk an
## A.39.3. Unique Seed
-Lalu bagaimana cara agar angka yang dihasilkan selalu berbeda setiap kali dipanggil? Apakah harus set ulang seed-nya? Jangan, karena kalau seed di-set ulang maka urutan deret random akan berubah. Seed hanya perlu di set sekali di awal. Lha, terus bagaimana?
+Lalu bagaimana cara agar angka yang dihasilkan selalu berbeda setiap kali dipanggil? Apakah harus set ulang seed-nya? Jangan, karena kalau seed di-set ulang maka urutan deret random akan berubah. Seed hanya perlu di set sekali di awal. Lalu apa solusi yang benar?
-Jadi begini, setiap kali `randomizer.Int()` dipanggil, hasilnya itu selalu berbeda, tapi sangat bisa diprediksi jika kita tau seed-nya, dan ini adalah masalah besar. Nah, ada cara agar angka random yang dihasilkan tidak berulang-ulang selalu contoh di-atas, caranya adalah menggunakan angka yang *unique*/unik sebagai seed, contohnya seperti angka [unix nano](https://en.wikipedia.org/wiki/GNU_nano) dari waktu sekarang.
+Jadi begini, setiap kali `randomizer.Int()` dipanggil, hasilnya itu selalu berbeda, tapi sangat bisa diprediksi jika kita tau seed-nya. Ada cara agar angka random yang dihasilkan tidak berulang-ulang seperti yang ada di contoh, caranya yaitu dengan menggunakan angka unik *unique*/unik sebagai seed, contohnya seperti angka [unix nano](https://en.wikipedia.org/wiki/GNU_nano) yang didapat dari informasi waktu sekarang.
Coba modifikasi program dengan kode berikut, lalu jalankan ulang. Jangan lupa meng-import package `time` ya.
@@ -68,7 +68,7 @@ fmt.Println("random ke-3:", randomizer.Int())
![Random Golang with unix nano seed](images/A_random_2.png)
-Bisa dilihat, setiap program dieksekusi angka random nya selalu berbeda, hal ini karena seed yang digunakan pasti berbeda satu sama lain saat program dijalankan. Seed-nya adalah angka unix nano dari waktu sekarang.
+Bisa dilihat, setiap program dieksekusi angka random nya selalu berbeda, hal ini karena seed yang digunakan pasti berbeda di setiap eksekusi program. Disitu seed yang digunakan adalah data numerik unix nano dari informasi waktu sekarang.
## A.39.4. Random Tipe Data Numerik Lainnya
@@ -89,11 +89,11 @@ lebih detailnya silakan merujuk ke https://golang.org/pkg/math/rand/
## A.39.5. Angka Random Index Tertentu
-Gunakan `randomizer.Intn(n)` untuk mendapatkan angka random dengan batas `0` hingga <`n`, contoh: `randomizer.Intn(100)` akan mengembalikan angka acak dari 0 hingga 99.
+Gunakan `randomizer.Intn(n)` untuk mendapatkan angka random dengan batas `0` hingga `n - 1`, contoh: `randomizer.Intn(100)` akan mengembalikan angka acak dari 0 hingga 99.
## A.39.6. Random Tipe Data String
-Untuk menghasilkan data random string, ada banyak cara yang bisa digunakan, salah satunya adalah dengan memafaatkan alfabet dan hasil random numerik.
+Untuk menghasilkan data random string, ada banyak cara yang bisa diterapkan, salah satunya adalah dengan memafaatkan alfabet dan hasil random numerik.
```go
var randomizer = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
diff --git a/content/A-reflect.md b/content/A-reflect.md
index 74ce3a853..dc6bb128c 100644
--- a/content/A-reflect.md
+++ b/content/A-reflect.md
@@ -1,13 +1,13 @@
# A.29. Reflect
-Reflection adalah teknik untuk inspeksi variabel, mengambil informasi dari variabel tersebut atau bahkan memanipulasinya. Cakupan informasi yang bisa didapatkan lewat reflection sangat luas, seperti melihat struktur variabel, tipe, nilai pointer, dan banyak lagi.
+Reflection adalah teknik untuk inspeksi variabel, mengambil informasi dari suatu variabel untuk dilihat metadatanya atau untuk keperluan manipulasi. Cakupan informasi yang bisa didapatkan lewat reflection sangat luas, seperti melihat struktur variabel, tipe, nilai pointer, dan banyak lagi.
Go menyediakan package `reflect`, berisikan banyak sekali fungsi untuk keperluan reflection. Pada chapter ini, kita akan belajar tentang dasar penggunaan package tersebut.
Dari banyak fungsi yang tersedia di dalam package tersebut, ada 2 fungsi yang paling penting untuk diketahui, yaitu `reflect.ValueOf()` dan `reflect.TypeOf()`.
-- Fungsi `reflect.ValueOf()` akan mengembalikan objek dalam tipe `reflect.Value`, yang berisikan informasi yang berhubungan dengan nilai pada variabel yang dicari.
-- Sedangkan `reflect.TypeOf()` mengembalikan objek dalam tipe `reflect.Type`. Objek tersebut berisikan informasi yang berhubungan dengan tipe data variabel yang dicari.
+- Fungsi `reflect.ValueOf()` akan mengembalikan objek dalam tipe `reflect.Value`, yang berisikan informasi yang berhubungan dengan nilai/data variabel yang diinspeksi.
+- Sedangkan `reflect.TypeOf()` mengembalikan objek dalam tipe `reflect.Type`. Objek tersebut berisikan informasi yang berhubungan dengan tipe data variabel yang diinspeksi.
## A.29.1. Mencari Tipe Data & Value Menggunakan Reflect
@@ -39,11 +39,11 @@ Objek `reflect.Value` memiliki beberapa method, salah satunya `Type()`. Method i
Statement `reflectValue.Int()` menghasilkan nilai `int` dari variabel `number`. Untuk menampilkan nilai variabel reflect, harus dipastikan dulu tipe datanya. Ketika tipe data adalah `int`, maka bisa menggunakan method `Int()`. Ada banyak lagi method milik struct `reflect.Value` yang bisa digunakan untuk pengambilan nilai dalam bentuk tertentu, contohnya: `reflectValue.String()` digunakan untuk mengambil nilai `string`, `reflectValue.Float64()` untuk nilai `float64`, dan lainnya.
-Perlu diketahui, fungsi yang digunakan harus sesuai dengan tipe data nilai yang ditampung variabel. Jika fungsi yang digunakan berbeda dengan tipe data variabelnya, maka akan menghasilkan error. Contohnya pada variabel menampung nilai bertipe `float64`, maka tidak bisa menggunakan method `String()`.
+Perlu diketahui, fungsi yang digunakan harus sesuai dengan tipe data nilai yang ditampung variabel. Jika fungsi yang digunakan berbeda dengan tipe data variabelnya, maka dipastikan muncul error. Contohnya seperti pada variabel yang nilainya bertipe `float64`, penggunaan method `String()` di situ pasti menghasilkan error.
-Diperlukan adanya pengecekan tipe data nilai yang disimpan, agar pengambilan nilai bisa tepat. Salah satunya bisa dengan cara seperti kode di atas, yaitu dengan mengecek dahulu apa jenis tipe datanya menggunakan method `Kind()`, setelah itu diambil nilainya dengan method yang sesuai.
+Diperlukan adanya pengecekan tipe data pada nilai yang disimpan, agar penggunaan method untuk pengambilan nilai bisa tepat. Salah satunya bisa dengan cara yang dicontohkan di atas, yaitu dengan mengecek dahulu apa jenis tipe datanya menggunakan method `Kind()`, setelah itu diambil nilainya dengan method yang sesuai.
-List konstanta tipe data dan method yang bisa digunakan dalam refleksi di Go bisa dilihat di https://godoc.org/reflect#Kind
+List konstanta tipe data dan method yang bisa digunakan dalam *reflection* di Go bisa dilihat di https://pkg.go.dev/reflect#Kind
## Pengaksesan Nilai Dalam Bentuk `interface{}`
@@ -57,7 +57,7 @@ fmt.Println("tipe variabel :", reflectValue.Type())
fmt.Println("nilai variabel :", reflectValue.Interface())
```
-Fungsi `Interface()` mengembalikan nilai interface kosong atau `interface{}`. Nilai aslinya sendiri bisa diakses dengan meng-casting interface kosong tersebut.
+Fungsi `Interface()` mengembalikan nilai interface kosong atau `interface{}` atau `any`. Nilai aslinya sendiri bisa diakses dengan meng-casting interface kosong tersebut menggunakan teknik *type assertion*.
```go
var nilai = reflectValue.Interface().(int)
@@ -65,7 +65,9 @@ var nilai = reflectValue.Interface().(int)
## A.29.2. Pengaksesan Informasi Property Variabel Objek
-Reflect bisa digunakan untuk mengambil informasi semua property variabel objek cetakan struct, dengan catatan property-property tersebut bermodifier public. Langsung saja kita praktekan, siapkan sebuah struct bernama `student`.
+Reflect API bisa digunakan untuk melihat metadata suatu property variabel objek cetakan struct, dengan catatan property-property tersebut bermodifier public. Contohnya bisa dilihat pada kode berikut.
+
+Siapkan sebuah struct bernama `student`.
```go
type student struct {
@@ -95,7 +97,7 @@ func (s *student) getPropertyInfo() {
}
```
-Terakhir, lakukan uji coba method di fungsi `main`.
+Terakhir, lakukan uji coba method `getPropertyInfo()` di fungsi `main()`.
```go
func main() {
@@ -106,13 +108,13 @@ func main() {
![Pengaksesan property menggunakan reflect](images/A_reflect_1_accessing_properties.png)
-Di dalam method `getPropertyInfo` terjadi beberapa hal. Pertama objek `reflect.Value` dari variabel `s` diambil. Setelah itu dilakukan pengecekan apakah variabel objek tersebut merupakan pointer atau tidak (bisa dilihat dari `if reflectValue.Kind() == reflect.Ptr`, jika bernilai `true` maka variabel adalah pointer). jika ternyata variabel memang pointer, maka perlu diambil objek reflect aslinya dengan cara memanggil method `Elem()`.
+Di dalam method `getPropertyInfo()` terjadi beberapa hal. Pertama objek `reflect.Value` dari variabel `s` diambil. Setelah itu dilakukan pengecekan apakah variabel objek tersebut merupakan pointer atau tidak (bisa dilihat dari `if reflectValue.Kind() == reflect.Ptr`, jika bernilai `true` maka variabel adalah pointer). jika ternyata variabel memang berisi pointer, maka perlu diambil data objek reflect aslinya via method `Elem()`.
-Setelah itu, dilakukan perulangan sebanyak jumlah property yang ada pada struct `student`. Method `NumField()` akan mengembalikan jumlah property publik yang ada dalam struct.
+Masih di dalam method `getPropertyInfo()`, dilakukan perulangan sebanyak jumlah property yang ada pada struct `student`. Method `NumField()` mengembalikan jumlah property publik yang ada dalam struct.
Di tiap perulangan, informasi tiap property struct diambil berurutan dengan lewat method `Field()`. Method ini ada pada tipe `reflect.Value` dan `reflect.Type`.
-- `reflectType.Field(i).Name` akan mengembalikan nama property
+- `reflectType.Field(i).Name` mengembalikan nama property
- `reflectType.Field(i).Type` mengembalikan tipe data property
- `reflectValue.Field(i).Interface()` mengembalikan nilai property dalam bentuk `interface{}`
@@ -122,7 +124,7 @@ Pengambilan informasi property, selain menggunakan indeks, bisa diambil berdasar
Informasi mengenai method juga bisa diakses lewat reflect, syaratnya masih sama seperti pada pengaksesan proprerty, yaitu harus bermodifier public.
-Pada contoh di bawah ini informasi method `SetName` akan diambil lewat reflection. Siapkan method baru di struct `student`, dengan nama `SetName`.
+Pada contoh di bawah ini informasi method `SetName()` akan diambil lewat reflection. Siapkan method baru di struct `student`, dengan nama `SetName()`.
```go
func (s *student) SetName(name string) {
@@ -130,7 +132,7 @@ func (s *student) SetName(name string) {
}
```
-Buat contoh penerapannya di fungsi `main`.
+Buat contoh penerapannya di fungsi `main()`.
```go
func main() {
@@ -149,11 +151,11 @@ func main() {
![Eksekusi method lewat reflection](images/A_reflect_2_accessing_method_information.png)
-Pada kode di atas, disiapkan variabel `s1` yang merupakan instance struct `student`. Awalnya property `Name` variabel tersebut berisikan string `"john wick"`.
+Pada kode di atas, disiapkan variabel `s1` yang merupakan instance struct `student`. Variabel tersebut memiliki property `Name` yang nilainya ditentukan di awal, yaitu string `"john wick"`.
-Setelah itu, refleksi nilai objek tersebut diambil, refleksi method `SetName` juga diambil. Pengambilan refleksi method dilakukan menggunakan `MethodByName` dengan argument adalah nama method yang diinginkan, atau bisa juga lewat indeks method-nya (menggunakan `Method(i)`).
+Setelah itu, data *reflection* nilai objek tersebut diambil, *reflection* method `SetName` juga diambil. Pengambilan *reflection* method dilakukan menggunakan `MethodByName` dengan argument adalah nama method yang diinginkan, atau bisa juga lewat indeks method-nya (menggunakan `Method(i)`).
-Setelah refleksi method yang dicari sudah didapatkan, `Call()` dipanggil untuk eksekusi method.
+Setelah *reflection* method yang dicari sudah didapatkan, `Call()` dipanggil untuk eksekusi method.
Jika eksekusi method diikuti pengisian parameter, maka parameternya harus ditulis dalam bentuk array `[]reflect.Value` berurutan sesuai urutan deklarasi parameter-nya. Dan nilai yang dimasukkan ke array tersebut harus dalam bentuk `reflect.Value` (gunakan `reflect.ValueOf()` untuk pengambilannya).
diff --git a/content/A-regex.md b/content/A-regex.md
index 0bc35c9ea..1503460c8 100644
--- a/content/A-regex.md
+++ b/content/A-regex.md
@@ -1,14 +1,14 @@
-# A.45. Regex
+# A.45. Regexp
-Regex atau regexp atau **regular expression** adalah suatu teknik yang digunakan untuk pencocokan string dengan pola tertentu. Regex biasa dimanfaatkan untuk pencarian dan pengubahan data string.
+Regexp atau regex atau **regular expression** adalah suatu teknik yang digunakan untuk pencocokan string yang memiliki pola tertentu. Regex biasa dimanfaatkan untuk pencarian dan pengubahan data string.
-Go mengadopsi standar regex **RE2**, untuk melihat sintaks yang di-support engine ini bisa langsung merujuk ke dokumentasinya di [https://github.com/google/re2/wiki/Syntax](https://github.com/google/re2/wiki/Syntax).
+Go mengadopsi spesifikasi regex **RE2**. Lebih detailnya mengenai RE2 bisa langsung cek dokumentasinya di [https://github.com/google/re2/wiki/Syntax](https://github.com/google/re2/wiki/Syntax).
Pada chapter ini kita akan belajar mengenai pengaplikasian regex dengan memanfaatkan fungsi-fungsi dalam package `regexp`.
## A.45.1. Penerapan Regexp
-Fungsi `regexp.Compile()` digunakan untuk mengkompilasi ekspresi regex. Fungsi tersebut mengembalikan objek bertipe `regexp.*Regexp`.
+Fungsi `regexp.Compile()` digunakan untuk mengkompilasi ekspresi regex. Fungsi tersebut mengembalikan objek bertipe `*regexp.Regexp`.
Berikut merupakan contoh penerapan regex untuk pencarian karakter.
@@ -36,13 +36,13 @@ func main() {
}
```
-Ekspresi `[a-z]+` maknanya adalah, semua string yang merupakan alphabet yang hurufnya kecil. Ekspresi tersebut di-compile oleh `regexp.Compile()` lalu disimpan ke variabel objek `regex` bertipe `regexp.*Regexp`.
+Ekspresi `[a-z]+` maknanya adalah semua string yang merupakan alphabet yang hurufnya kecil. Ekspresi tersebut di-compile oleh `regexp.Compile()` lalu disimpan ke variabel objek `regex` bertipe `*regexp.Regexp`.
Struct `regexp.Regexp` memiliki banyak method, salah satunya adalah `FindAllString()`, berfungsi untuk mencari semua string yang sesuai dengan ekspresi regex, dengan kembalian berupa slice string.
-Jumlah hasil pencarian dari `regex.FindAllString()` bisa ditentukan. Contohnya pada `res1`, ditentukan maksimal `2` data saja pada nilai kembalian. Jika batas di set `-1`, maka akan mengembalikan semua data.
+Jumlah hasil pencarian dari `regex.FindAllString()` bisa ditentukan. Contohnya pada `res1`, ditentukan maksimal `2` data saja pada nilai kembalian. Jika batas di set `-1`, maka semua hasil yang cocok dikembalikan oleh fungsi tersebut.
-Ada cukup banyak method struct `regexp.*Regexp` yang bisa kita manfaatkan untuk keperluan pengelolaan string. Berikut merupakan pembahasan tiap method-nya.
+Ada cukup banyak method struct `*regexp.Regexp` yang bisa kita manfaatkan untuk keperluan pengelolaan string. Berikut merupakan pembahasan tiap method-nya.
## A.45.2. Method `MatchString()`
@@ -143,7 +143,7 @@ fmt.Println(str)
// "banana potato soup"
```
-Pada contoh di atas, jika salah satu substring yang *match* adalah `"burger"` maka akan diganti dengan `"potato"`, string selainnya tidak di replace.
+Pada contoh di atas, jika ada substring yang *match* dengan kata `"burger"`, maka akan diganti dengan `"potato"`.
## A.45.8. Method `Split()`
diff --git a/content/A-seleksi-kondisi.md b/content/A-seleksi-kondisi.md
index 647b8040c..e5906b979 100644
--- a/content/A-seleksi-kondisi.md
+++ b/content/A-seleksi-kondisi.md
@@ -1,12 +1,12 @@
# A.13. Seleksi Kondisi
-Seleksi kondisi digunakan untuk mengontrol alur program. Analoginya mirip seperti fungsi rambu lalu lintas di jalan raya. Kapan kendaraan diperbolehkan melaju dan kapan harus berhenti diatur oleh rambu tersebut. Seleksi kondisi pada program juga kurang lebih sama, kapan sebuah blok kode akan dieksekusi dikontrol.
+Seleksi kondisi digunakan untuk mengontrol alur eksekusi flow program. Analoginya mirip seperti fungsi rambu lalu lintas di jalan raya. Kapan kendaraan diperbolehkan melaju dan kapan harus berhenti diatur oleh rambu tersebut. Seleksi kondisi pada program juga kurang lebih sama, kapan sebuah blok kode dieksekusi dikontrol.
Yang dijadikan acuan oleh seleksi kondisi adalah nilai bertipe `bool`, bisa berasal dari variabel, ataupun hasil operasi perbandingan. Nilai tersebut menentukan blok kode mana yang akan dieksekusi.
-Go memiliki 2 macam keyword untuk seleksi kondisi, yaitu **if else** dan **switch**. Pada chapter ini kita akan mempelajarinya satu-persatu.
+Go memiliki 2 macam keyword untuk seleksi kondisi, yaitu **if else** dan **switch**. Pada chapter ini kita akan mempelajari keduanya.
-> Go tidak mendukung seleksi kondisi menggunakan **ternary**.
Statement seperti: `var data = (isExist ? "ada" : "tidak ada")` adalah invalid dan menghasilkan error.
+> Go tidak mendukung seleksi kondisi menggunakan **ternary**.
Statement seperti `var data = (isExist ? "ada" : "tidak ada")` adalah invalid dan menghasilkan error.
## A.13.1. Seleksi Kondisi Menggunakan Keyword `if`, `else if`, & `else`
@@ -30,13 +30,11 @@ Dari ke-empat kondisi di atas, yang terpenuhi adalah `if point > 5`, karena nila
![Seleksi kondisi `if` - `else`](images/A_seleksi_kondisi_1_if_else.png)
-Skema if else Go sama seperti pada pemrograman umumnya. Yaitu di awal seleksi kondisi menggunakan `if`, dan ketika kondisinya tidak terpenuhi akan menuju ke `else` (jika ada). Ketika ada banyak kondisi, gunakan `else if`.
-
-> Di bahasa pemrograman lain, ketika ada seleksi kondisi yang isi blok-nya hanya 1 baris saja, kurung kurawal boleh tidak dituliskan. Berbeda dengan aturan di Go, kurung kurawal harus tetap dituliskan meski isinya hanya 1 blok satement.
+Penulisan if else Go diawali dengan keyword `if` kemudian diikuti nilai seleksi kondisi dan blok kode ketika kondisi terpenuhi. Ketika kondisinya tidak terpenuhi akan blok kode `else` dipanggil (jika blok kode `else` tersebut ada). Ketika ada banyak kondisi, gunakan `else if`.
## A.13.2. Variabel Temporary Pada `if` - `else`
-Variabel temporary adalah variabel yang hanya bisa digunakan pada blok seleksi kondisi di mana ia ditempatkan saja. Penggunaan variabel ini membawa beberapa manfaat, antara lain:
+Variabel temporary adalah variabel yang hanya bisa digunakan pada deretan blok seleksi kondisi di mana ia ditempatkan. Penggunaan variabel ini membawa beberapa manfaat, antara lain:
- Scope atau cakupan variabel jelas, hanya bisa digunakan pada blok seleksi kondisi itu saja
- Kode menjadi lebih rapi
@@ -54,9 +52,9 @@ if percent := point / 100; percent >= 100 {
}
```
-Variabel `percent` nilainya didapat dari hasil perhitungan, dan hanya bisa digunakan di deretan blok seleksi kondisi itu saja.
+Variabel `percent` nilainya didapat dari hasil perhitungan, dan hanya bisa digunakan di deretan blok seleksi kondisi itu saja yang mencakup blok `if`, `else if`, dan `else`.
-> Deklarasi variabel temporary hanya bisa dilakukan lewat metode type inference yang menggunakan tanda `:=`. Penggunaan keyword `var` di situ tidak diperbolehkan karena akan menyebabkan error.
+> Deklarasi variabel temporary hanya bisa dilakukan lewat metode type inference yang menggunakan tanda `:=`. Penggunaan keyword `var` di situ tidak diperbolehkan karena menyebabkan error.
## A.13.3. Seleksi Kondisi Menggunakan Keyword `switch` - `case`
@@ -77,7 +75,7 @@ default:
Pada kode di atas, tidak ada kondisi atau `case` yang terpenuhi karena nilai variabel `point` tetap `6`. Ketika hal seperti ini terjadi, blok kondisi `default` dipanggil. Bisa dibilang bahwa `default` merupakan `else` dalam sebuah switch.
-Perlu diketahui, switch pada pemrograman Go memiliki perbedaan dibanding bahasa lain. Di Go, ketika sebuah case terpenuhi, tidak akan dilanjutkan ke pengecekan case selanjutnya, meskipun tidak ada keyword `break` di situ. Konsep ini berkebalikan dengan switch pada umumnya, yang ketika sebuah case terpenuhi, maka akan tetap dilanjut mengecek case selanjutnya kecuali ada keyword `break`.
+Perlu diketahui, switch pada pemrograman Go memiliki perbedaan dibanding bahasa lain. Di Go, ketika sebuah case terpenuhi, tidak akan dilanjutkan ke pengecekan case selanjutnya, meskipun tidak ada keyword `break` di situ. Konsep ini berkebalikan dengan switch pada umumnya pemrograman lain (yang ketika sebuah case terpenuhi, maka akan tetap dilanjut mengecek case selanjutnya kecuali ada keyword `break`).
## A.13.4. Pemanfaatan `case` Untuk Banyak Kondisi
@@ -100,7 +98,7 @@ Kondisi `case 7, 6, 5, 4:` akan terpenuhi ketika nilai variabel `point` adalah 7
## A.13.5. Kurung Kurawal Pada Keyword `case` & `default`
-Tanda kurung kurawal (`{ }`) bisa diterapkan pada keyword `case` dan `default`. Tanda ini opsional, boleh dipakai boleh tidak. Bagus jika dipakai pada blok kondisi yang di dalamnya ada banyak statement, kode akan terlihat lebih rapi dan mudah di-maintain.
+Tanda kurung kurawal (`{ }`) bisa diterapkan pada keyword `case` dan `default`. Tanda ini opsional, boleh dipakai boleh tidak. Bagus jika dipakai pada blok kondisi yang di dalamnya ada banyak statement, dengannya kode akan terlihat lebih rapi.
Perhatikan kode berikut, bisa dilihat pada keyword `default` terdapat kurung kurawal yang mengapit 2 statement di dalamnya.
@@ -146,7 +144,7 @@ default:
Seperti yang sudah dijelaskan sebelumnya, bahwa switch pada Go memiliki perbedaan dengan bahasa lain. Ketika sebuah `case` terpenuhi, pengecekan kondisi tidak akan diteruskan ke case-case setelahnya.
-Keyword `fallthrough` digunakan untuk memaksa proses pengecekan diteruskan ke satu `case` selanjutnya dengan **tanpa menghiraukan nilai kondisinya**, jadi satu case di pengecekan selanjutnya tersebut selalu dianggap benar (meskipun aslinya adalah salah). Dalam sebuah `switch` lebih dari satu `fallthrough` bisa di tempatkan untuk memaksa melanjutkan proses pengecekan ke satu `case` setelahnya.
+Keyword `fallthrough` digunakan untuk memaksa proses pengecekan tetap diteruskan ke `case` selanjutnya dengan **tanpa menghiraukan nilai kondisinya**, efeknya adalah case di pengecekan selanjutnya selalu dianggap `true` (meskipun aslinya bisa saja kondisi tersebut tidak terpenuhi, akan tetap dianggap `true`).
```go
var point = 6
@@ -167,11 +165,12 @@ default:
}
```
-
-Setelah pengecekan `case (point < 8) && (point > 3)` selesai, akan dilanjut ke pengecekan `case point < 5`, karena ada `fallthrough` di situ.
+Di contoh, setelah pengecekan `case (point < 8) && (point > 3)` selesai, dilanjut ke pengecekan `case point < 5`, karena ada `fallthrough` di situ. Dan kondisi `case < 5` tersebut dianggap `true` meskipun secara logika harusnya tidak terpenuhi.
![Penggunaan `fallthrough` dalam `switch`](images/A_seleksi_kondisi_2_fallthrough.png)
+Pada `case` dalam sebuah `switch`, diperbolehkan terdapat lebih dari satu `fallthrough`.
+
## A.13.8. Seleksi Kondisi Bersarang
Seleksi kondisi bersarang adalah seleksi kondisi, yang berada dalam seleksi kondisi, yang mungkin juga berada dalam seleksi kondisi, dan seterusnya. Seleksi kondisi bersarang bisa dilakukan pada `if` - `else`, `switch`, ataupun kombinasi keduanya.
diff --git a/content/A-setup-go-project-dengan-go-modules.md b/content/A-setup-go-project-dengan-go-modules.md
index 31ece0485..ab65a440c 100644
--- a/content/A-setup-go-project-dengan-go-modules.md
+++ b/content/A-setup-go-project-dengan-go-modules.md
@@ -1,31 +1,28 @@
# A.3. Go Modules
-Pada bagian ini kita akan belajar cara inisialisasi project menggunakan Go Modules (atau Modules).
+Pada bagian ini kita akan belajar cara pembuatan project baru menggunakan Go Modules.
## A.3.1. Penjelasan
-Go modules merupakan manajemen dependensi resmi untuk Go. Modules ini diperkenalkan pertama kali di `go1.11`, sebelum itu pengembangan project Go dilakukan dalam `GOPATH`.
+Go modules merupakan tools untuk manajemen dependensi resmi milik Go. Modules digunakan untuk menginisialisasi sebuah project, sekaligus melakukan manajemen terhadap *3rd party* atau *library* atau *dependency* yang digunakan dalam project.
-Modules digunakan untuk menginisialisasi sebuah project, sekaligus melakukan manajemen terhadap *3rd party* atau *library* lain yang dipergunakan.
+Modules penggunaannya adalah via CLI. Jika pembaca sudah sukses meng-*install* Go, maka otomatis bisa menggunakan operasi CLI Go Modules.
-Modules penggunaannya adalah lewat CLI. Dan jika temen-temen sudah sukses meng-*install* Go, maka otomatis bisa mempergunakan Go Modules.
-
-> Modules atau Module di sini merupakan istilah untuk project ya. Jadi jangan bingung.
+> Di Go, istilah modules (atau module) maknanya adalah sama dengan project. Jadi gak perlu bingung
## A.3.2. Inisialisasi Project Menggunakan Go Modules
Command `go mod init` digunakan untuk menginisialisasi project baru.
-Mari kita praktekan, buat folder baru, bisa via CLI atau lewat browser/finder.
+Mari langsung praktekan saja. Buat folder baru, bisa via CLI atau lewat browser/finder.
```bash
mkdir project-pertama
cd project-pertama
go mod init project-pertama
-dir
```
-Bisa dilihat pada *command* di atas ada direktori `project-pertama`, dibuat. Setelah masuk ke direktori tersebut, perintah `go mod init project-pertama` dijalankan. Dengan ini maka kita telah menginisialisasi direktori `project-pertama` sebagai sebuah project Go dengan nama `project-pertama` (kebetulan di sini nama project sama dengan nama direktori-nya).
+Bisa dilihat pada *command* di atas ada direktori `project-pertama`, dibuat. Setelah masuk ke direktori tersebut, perintah `go mod init project-pertama` dijalankan. Dengan ini maka kita telah menginisialisasi direktori/folder `project-pertama` sebagai sebuah project Go dengan nama `project-pertama`.
![Init project](images/A_go_modules_1_initmodule.png)
@@ -36,17 +33,21 @@ go mod init
go mod init project-pertama
```
-Untuk nama project, umumnya disamakan dengan nama direktori, tapi bisa saja sebenarnya menggunakan nama yang lain.
+Di sini kita tentukan nama project adalah sama dengan nama folder, ini merupakan *best practice* di Go.
+
+> Nama project dan nama module merupakan artinya adalah sama. Ingat, module adalah sama dengan project
-> Nama project dan Nama module merupakan istilah yang sama.
+Eksekusi perintah `go mod init` menghasilkan satu buah file baru bernama `go.mod`. File ini digunakan oleh Go toolchain untuk menandai bahwa folder di mana file tersebut berada adalah folder project. Jadi pastikan untuk tidak menghapus file tersebut.
-Eksekusi perintah `go mod init` menghasilkan satu buah file baru bernama `go.mod`. File ini digunakan oleh Go toolchain untuk menandai bahwa folder di mana file tersebut berada adalah folder project. Jadi jangan dihapus ya file tersebut.
+Ok, sekian. Cukup itu saja cara inisialisasi project di Go.
---
-Ok, sekian. Cukup itu saja cara inisialisasi project di Go.
+O iya, sebenarnya selain Go Modules, setup project di Go juga bisa menggunakan `$GOPATH` yang pembahasannya ada di chapter ([A.4. Setup GOPATH Dan Workspace](/A-gopath-dan-workspace.html)).
+
+Namun metode inisialisasi project via GOPATH sudah outdate dan kurang dianjurkan untuk project-project yang dikembangkan menggunakan Go versi terbaru (1.14 ke atas).
-O iya, sebenarnya selain Go Modules, setup project di Go juga bisa menggunakan `$GOPATH` ([A.4. Setup GOPATH Dan Workspace](/A-gopath-dan-workspace.html)). Tapi inisialisasi project dengan GOPATH sudah outdate dan kurang dianjurkan untuk project-project yang dikembangkan menggunakan Go versi terbaru (1.14 ke atas). Jadi setelah chapter ini, bisa langsung lanjut ke [A. Instalasi Editor](/A-instalasi-editor.html).
+Jadi setelah chapter ini, penulis boleh langsung lompat ke pembahasan di chapter [A.5. Instalasi Editor](/A-instalasi-editor.html).
---
diff --git a/content/A-simplified-fan-in-fan-out-pipeline.md b/content/A-simplified-fan-in-fan-out-pipeline.md
index c01ada997..e3ecc0229 100644
--- a/content/A-simplified-fan-in-fan-out-pipeline.md
+++ b/content/A-simplified-fan-in-fan-out-pipeline.md
@@ -6,7 +6,7 @@ Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-
Pada chapter ini kita akan mempelajari concurrency pattern juga, lanjutan dari sebelumnya. Pada versi ini kalau dilihat dari perspektif coding penerapannya akan lebih ringkas. Tapi apakah lebih mudah dan lebih *performant* dibanding penerapan pipeline sebelumnya? Jawabannya sangat tergantung dengan kasus yang dihadapi, tergantung spesifikasi hardware-nya juga, dan mungkin juga tergantung dengan taste dari si engineer pembuat program.
-Perbedaannya sebenarnya hanya pada bagian Fan-out Fan-in nya saja. Di sini (hampir) semua pipeline isinya adalah gabungan dari Fan-out dan juga Fan-in. Jadi kita tidak perlu report *merge*. Dan juga beda lainnya adalah, jumlah worker bisa kita tentukan sesuai kebutuhan (*parameterized*).
+Kalau dilihat lebih dalam, perbedaannya sebenarnya hanya pada bagian Fan-out Fan-in nya saja. Di metode ini (hampir) semua pipeline isinya adalah gabungan dari Fan-out dan juga Fan-in. Jadi kita tidak perlu report *merge*. Selain itu, di sini kita bisa dengan mudah mengatur jumlah worker sesuai kebutuhan.
Ok, agar lebih jelas mari kita mulai praktek.
@@ -16,11 +16,11 @@ Kita akan modifikasi file program `1-dummy-file-generator.go` yang pada chapter
## A.63.2. Program Generate Dummy File *Sequentially*
-Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program versi sekuensialnya. Bisa langsung copy-paste, atau tulis dari awal dengan mengikut tutorial ini secara keseluruhan. Untuk penjelasan detail program versi sekuensial silakan merujuk ke chapter sebelumnya saja, di sini kita tulis langsung agar bisa cepat dimulai bagian program konkuren.
+Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program versi sekuensialnya. Bisa langsung copy-paste, atau tulis dari awal dengan mengikut tutorial ini secara keseluruhan. Untuk penjelasan detail program versi sekuensial silakan cek saja di chapter sebelumnya saja, di sini kita tulis langsung agar bisa cepat dimulai bagian program konkuren.
Siapkan folder project baru, isinya satu buah file `1-generate-dummy-files-sequentially.go`.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
```go
package main
@@ -40,7 +40,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.60-worker-pool")
```
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -54,7 +54,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -71,7 +71,7 @@ func randomString(length int) string {
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -101,9 +101,9 @@ Kita lanjut dulu saja. Berikut adalah output jika program di atas di-run.
## A.63.3. Program Generate Dummy File *Concurrently*
-Sekarang saya buat file program `2-generate-dummy-files-concurrently.go` yang isinya adalah sama yaitu untuk keperluan generate dummy files, tapi pembuatannya dilakukan secara konkuren.
+Selanjutnya, buat file program `2-generate-dummy-files-concurrently.go` yang isinya adalah sama yaitu untuk keperluan generate dummy files tapi dilakukan secara konkuren.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
Import beberapa hal pada file baru ini, lalu definisikan beberapa variabel juga.
@@ -126,7 +126,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.60-worker-pool")
```
-#### • Definisi struct `FileInfo`
+#### ◉ Definisi struct `FileInfo`
Kita perlu siapkan struct baru bernama `FileInfo`, struct ini digunakan sebagai skema payload data ketika dikirimkan via channel dari goroutine jobs ke goroutine worker.
@@ -143,7 +143,7 @@ type FileInfo struct {
* Property `WorkerIndex` digunakan sebagai penanda worker mana yang akan melakukan operasi pembuatan file tersebut.
* Property `Err` default isinya kosong. Nantinya akan diisi dengan objek error ketika ada error saat pembuatan file.
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -157,7 +157,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -173,7 +173,7 @@ func randomString(length int) string {
}
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -203,7 +203,7 @@ func generateFiles() {
}
```
-Isi fungsi generate files ini secara garis besar ada 3:
+Secara garis besar, isi fungsi generate files ini ada 3:
* Pipeline 1, bertugas men-*dispatch* goroutine untuk distribusi jobs.
* Pipeline 2, bertugas men-*dispatch* goroutine untuk start worker yang masing-masing worker punya tugas utama yaitu membuat files.
@@ -211,11 +211,11 @@ Isi fungsi generate files ini secara garis besar ada 3:
Fungsi `generateFileIndexes()` nantinya akan mengembalikan channel `chanFileIndex` yang fungsi dari channel ini adalah untuk media komunikasi antara proses dalam fungsi `generateFileIndexes()` (yaitu distribusi jobs) dengan proses dalam fungsi selanjutnya yaitu `createFiles()`.
-Fungsi `createFiles()` di sini merupakan fungsi **Fan-out Fan-in** karena menerima parameter channel pipeline sebelumnya, lalu kemudian min-dispatch goroutine worker dan menge-track output dari masing-masing worker ke channel output. Jadi fungsi `createFiles()` ini merupakan gabungan dari fungsi Fan-out dan Fan-in (proses merge channel output dari Fan-out juga ada di dalam fungsi tersebut).
+Fungsi `createFiles()` di sini merupakan fungsi **Fan-out Fan-in** karena menerima parameter channel pipeline sebelumnya, kemudian min-dispatch goroutine worker dan melacak output dari masing-masing worker ke channel output. Bisa dibilang fungsi `createFiles()` merupakan gabungan dari fungsi Fan-out dan Fan-in (proses merge channel output dari Fan-out juga ada di dalam fungsi tersebut).
Fungsi `createFiles()` menghasilkan channel yang isinya merupakan result dari operasi tiap-tiap jobs. Dari data yang dilewatkan via channel tersebut akan ketahuan misal ada error atau tidak saat pembuatan files. Channel tersebut kemudian di-loop lalu ditampilkan tiap-tiap result-nya.
-#### • Fungsi `generateFileIndexes()`
+#### ◉ Fungsi `generateFileIndexes()`
Fungsi ini merupakan fungsi Fan-out distribusi jobs. Di dalamnya dilakukan perulangan sejumlah `totalFile`, kemudian data tiap index digunakan untuk pembentukan filename lalu dikirim ke channel outputnya.
@@ -239,11 +239,11 @@ func generateFileIndexes() <-chan FileInfo {
Setelah dipastikan semua job terkirim, kita close channel output `chanOut` tersebut.
-#### • Fungsi `dispatchWorkers()`
+#### ◉ Fungsi `dispatchWorkers()`
Bagian ini merupakan yang paling butuh *effort* untuk dipahami. Jadi fungsi `createFiles()` seperti yang sudah saja jelaskan secara singkat di atas, fungsi ini merupakan fungsi gabungan Fan-out (menerima channel output dari pipeline sebelumnya) dan juga Fan-in (menjalankan beberapa worker untuk memproses channel output dari pipeline sebelumnya, lalu output masing-masing worker yang juga merupakan channel - langsung di merge jadi satu channel saja).
-Mungkin lebih enak silakan tulis dulu fungsinya, lalu kita bahas satu per satu setelahnya.
+Mungkin lebih enak silakan tulis dulu fungsinya, kemudian kita bahas satu per satu.
```go
func createFiles(chanIn <-chan FileInfo, numberOfWorkers int) <-chan FileInfo {
@@ -309,27 +309,27 @@ Penjelasan:
Semoga cukup jelas ya. Kelebihan metode ini ini salah satunya adalah kita bisa dengan mudah menentukan jumlah workernya.
-> Untuk temen-temen yang bingung, mungkin fungsi ini bisa dipecah menjadi satu fungsi Fan-out dan satu fungsi Fan-in seperti chapter sebelumnya.
+> Untuk pembaca yang bingung, mungkin fungsi ini bisa dipecah menjadi satu fungsi Fan-out dan satu fungsi Fan-in seperti chapter sebelumnya.
## A.63.4. Test Eksekusi Program
Saya akan coba jalankan program pertama dan kedua, lalu mari kita lihat perbedaannya.
-#### • Program Generate Dummy File *Sequentially*
+#### ◉ Program Generate Dummy File *Sequentially*
![Generate dummy files sequentially](images/A_simplified_fan_in_fan_out_pipeline_2_benchmark.png)
Testing di awal chapter ini hasilnya butuh sekitar **19 detik** untuk menyelesaikan generate dummy files sebanyak 3000 secara sekuensial. Tapi kali ini lebih lambat, yaitu **23 detik** dan ini wajar, karena di tiap operasi kita munculkan log ke stdout (via `log.Println()`).
-#### • Program Generate Dummy File *Concurrently*
+#### ◉ Program Generate Dummy File *Concurrently*
![Generate dummy files concurrently](images/A_simplified_fan_in_fan_out_pipeline_3_concurrent.png)
Bandingkan dengan ini, **3 detik** saja! luar biasa sekali bukan beda performanya. Dan pastinya akan lebih cepat lagi kalau kita hapus statement untuk logging ke stdout (`log.Println()`).
-Nah dari sini semoga cukup jelas ya bedanya kalau dari sisi performa. Inilah pentingnya kenapa konkurensi di Go harus diterapkan (untuk kasus yang memang bisa di-konkurensikan prosesnya). Tapi temen-temen juga harus hati-hati dalam mendesain pipeline dan menentukan jumlah workernya, karena jika tidak tepat bisa makan *resources* seperti CPU dan RAM cukup tinggi.
+Nah dari sini semoga cukup jelas ya bedanya kalau dari sisi performa. Inilah pentingnya kenapa konkurensi di Go harus diterapkan (untuk kasus yang memang bisa di-konkurensikan prosesnya). Tapi pembaca juga harus hati-hati dalam mendesain pipeline dan menentukan jumlah workernya, karena jika tidak tepat bisa makan *resources* seperti CPU dan RAM cukup tinggi, efeknya bisa terjadi bottleneck yang mempengaruhi performa program secara menyeluruh.
-> Untuk menentukan jumlah worker yang ideal, caranya adalah dengan coba-coba dan disesuaikan dengan spesifikasi server/laptopnya. Jadi tidak ada angka yang pasti berapa jumlah worker ideal. Sangat tergantung ke banyak hal (jenis proses, jumlah pipeline, jumlah worker per pipeline, spesifikasi hardware, dsb).
+> Untuk menentukan jumlah worker yang ideal, perlu banya coba-coba dan perlu dipertimbangkan juga faktor spesifikasi server/laptopnya. Jadi tidak ada angka yang pasti berapa jumlah worker ideal karena ada banyak faktor yang mempengaruhi (jenis proses, jumlah pipeline, jumlah worker per pipeline, spesifikasi hardware, dsb).
---
diff --git a/content/A-slice.md b/content/A-slice.md
index 6ab751b88..d459aa7df 100644
--- a/content/A-slice.md
+++ b/content/A-slice.md
@@ -1,6 +1,6 @@
# A.16. Slice
-**Slice** adalah *reference* elemen array. Slice bisa dibuat, atau bisa juga dihasilkan dari manipulasi sebuah array ataupun slice lainnya. Karena merupakan data *reference*, menjadikan perubahan data di tiap elemen slice akan berdampak pada slice lain yang memiliki alamat memori yang sama.
+**Slice** adalah *reference* elemen array. Slice bisa dibuat, atau bisa juga dihasilkan dari manipulasi sebuah array ataupun slice lainnya. Karena slice merupakan data *reference*, menjadikan perubahan data di tiap elemen slice akan berdampak pada slice lain yang memiliki alamat memori yang sama.
## A.16.1. Inisialisasi Slice
@@ -40,9 +40,9 @@ Kode `fruits[0:2]` maksudnya adalah pengaksesan elemen dalam slice `fruits` yan
Ketika mengakses elemen array menggunakan satu buah indeks (seperti `data[2]`), nilai yang didapat merupakan hasil **copy** dari referensi aslinya. Berbeda dengan pengaksesan elemen menggunakan 2 indeks (seperti `data[0:2]`), nilai yang didapat adalah *reference* elemen atau slice.
-> Tidak apa jikalau pembaca masih bingung, di bawah akan dijelaskan lebih mendetail lagi tentang slice dan *reference*
+> Sampai sini tidak apa jika pembaca masih bingung. Sebentar lagi kita akan bahas lebih detail lagi tentang penerapan slice dan *reference*
-Tabel berikut adalah list operasi operasi menggunakan teknik 2 indeks yang bisa dilakukan.
+Tabel berikut berisi contoh macam-macam operasi slice (atau *slicing*) menggunakan teknik 2 indeks yang bisa dilakukan di Go.
```go
var fruits = []string{"apple", "grape", "banana", "melon"}
@@ -115,7 +115,7 @@ fmt.Println(len(fruits)) // 4
## A.16.5. Fungsi `cap()`
-Fungsi `cap()` digunakan untuk menghitung lebar atau kapasitas maksimum slice. Nilai kembalian fungsi ini untuk slice yang baru dibuat pasti sama dengan `len`, tapi bisa berubah seiring operasi slice yang dilakukan. Agar lebih jelas, silakan disimak kode berikut.
+Fungsi `cap()` digunakan untuk menghitung lebar atau kapasitas maksimum slice. Nilai kembalian fungsi ini untuk slice yang baru dibuat pasti sama dengan `len`, tapi bisa berubah seiring operasi slice yang dilakukan. Agar lebih jelas, silakan pelajari kode berikut.
```go
var fruits = []string{"apple", "grape", "banana", "melon"}
@@ -233,7 +233,7 @@ Jika dilihat pada kode di atas, isi `dst` masih tetap 3 elements, tapi dua eleme
## A.16.8. Pengaksesan Elemen Slice Dengan 3 Indeks
-**3 index** adalah teknik slicing elemen yang sekaligus menentukan kapasitasnya. Cara menggunakannnya yaitu dengan menyisipkan angka kapasitas di belakang, seperti `fruits[0:1:1]`. Angka kapasitas yang diisikan tidak boleh melebihi kapasitas slice yang akan di slicing.
+**3 index** adalah teknik slicing untuk pengaksesan elemen yang sekaligus menentukan kapasitasnya. Cara penggunaannya yaitu dengan menyisipkan angka kapasitas di belakang, seperti `fruits[0:1:1]`. Angka kapasitas yang diisikan tidak boleh melebihi kapasitas slice yang akan di slicing.
Berikut merupakan contoh penerapannya.
diff --git a/content/A-sql.md b/content/A-sql.md
index dd3f0b1fd..d2464aadb 100644
--- a/content/A-sql.md
+++ b/content/A-sql.md
@@ -1,17 +1,18 @@
# A.56. SQL
-Go menyediakan package `database/sql` berisikan generic interface untuk keperluan interaksi dengan database sql. Package ini hanya bisa digunakan ketika **driver** database engine yang dipilih juga ada.
+Go menyediakan package `database/sql` berisikan generic interface untuk keperluan interaksi dengan database sql. Package ini mewajibkan pengguna untuk juga menggunakan **driver** database engine yang dipilih.
-Ada cukup banyak sql driver yang tersedia untuk Go, detailnya bisa diakses di [https://github.com/golang/go/wiki/SQLDrivers](https://github.com/golang/go/wiki/SQLDrivers). Beberapa di antaranya:
+Ada cukup banyak sql driver yang tersedia untuk Go, detailnya bisa diakses di [https://go.dev/wiki/SQLDrivers](https://go.dev/wiki/SQLDrivers). Beberapa di antaranya:
- - MySql
+ - MySQL / MariaDB
- Oracle
- - MS Sql Server
+ - MS SQL Server
+ - Postgres
- dan lainnya
-Driver-driver tersebut merupakan project open source yang diinisiasi oleh komunitas di Github. Artinya kita selaku developer juga bisa ikut berkontribusi di dalamnya.
+Driver-driver tersebut merupakan project open source yang diinisiasi oleh komunitas di Github. Kita yang juga seorang developer juga bisa ikut berkontribusi di sana.
-Pada chapter ini kita akan belajar bagaimana berkomunikasi dengan database MySQL menggunakan driver [Go MySQL Driver](https://github.com/go-sql-driver/mysql).
+Pada chapter ini kita akan belajar bagaimana membuat Go bisa berkomunikasi dengan database MySQL menggunakan driver [Go MySQL Driver](https://github.com/go-sql-driver/mysql).
## A.56.1. Instalasi Driver
@@ -26,9 +27,9 @@ go get github.com/go-sql-driver/mysql
## A.56.2. Setup Database
-> Sebelumnya pastikan sudah ada [mysql server](https://dev.mysql.com/downloads/mysql/) yang terinstal di lokal anda.
+Sebelumnya mulai, pastikan sudah ada [mysql server](https://dev.mysql.com/downloads/mysql/) yang terinstal dan jalan di lokal environment pembaca.
-Buat database baru bernama `db_belajar_golang`, dan tabel baru bernama `tb_student`.
+Jika database server sudah siap, buat database baru bernama `db_belajar_golang`, dan tabel baru bernama `tb_student`.
```sql
CREATE TABLE IF NOT EXISTS `tb_student` (
@@ -81,9 +82,10 @@ func connect() (*sql.DB, error) {
}
```
-Fungsi `sql.Open()` digunakan untuk memulai koneksi dengan database. Fungsi tersebut memiliki 2 parameter mandatory, nama driver dan **connection string**.
+Fungsi `sql.Open()` digunakan untuk memulai koneksi dengan database. Fungsi tersebut memiliki 2 parameter mandatory yang harus diisi, yaitu nama driver dan **connection string**.
Skema connection string untuk driver mysql yang kita gunakan cukup unik, `root@tcp(127.0.0.1:3306)/db_belajar_golang`. Di bawah ini merupakan skema connection string yang bisa digunakan pada driver Go MySQL Driver. Jika anda menggunakan driver mysql lain, skema koneksinya bisa saja berbeda tergantung driver yang digunakan.
+
```
user:password@tcp(host:port)/dbname
user@tcp(host:port)/dbname
@@ -146,7 +148,9 @@ func sqlQuery() {
Setiap kali terbuat koneksi baru, jangan lupa untuk selalu **close** instance koneksinya. Bisa menggunakan keyword `defer` seperti pada kode di atas, `defer db.Close()`.
-Fungsi `db.Query()` digunakan untuk eksekusi sql query. Fungsi tersebut parameter keduanya adalah variadic, sehingga boleh tidak diisi. Pada kode di atas bisa dilihat bahwa nilai salah satu clause `where` adalah tanda tanya (`?`). Tanda tersebut kemudian akan ter-replace oleh nilai pada parameter setelahnya (nilai variabel `age`). Teknik penulisan query sejenis ini sangat dianjurkan, untuk mencegah [sql injection](https://en.wikipedia.org/wiki/SQL_injection).
+Fungsi `db.Query()` digunakan untuk eksekusi sql query. Fungsi tersebut memiliki parameter ke-2 berbentuk variadic, jadi boleh tidak diisi.
+
+Pada kode di atas bisa dilihat bahwa nilai salah satu clause `where` adalah tanda tanya (`?`). Tanda tersebut kemudian akan di-replace oleh nilai pada argument parameter setelahnya (nilai variabel `age`). Teknik penulisan query sejenis ini sangat dianjurkan, untuk mencegah optensi serangan [sql injection](https://en.wikipedia.org/wiki/SQL_injection).
Fungsi tersebut menghasilkan instance bertipe `sql.*Rows`, yang juga perlu di **close** ketika sudah tidak digunakan (`defer rows.Close()`).
@@ -174,13 +178,13 @@ func main() {
}
```
-Output:
+Output program:
![Membaca data dari database server](images/A_sql_2_sql_query.png)
## A.56.4. Membaca 1 Record Data Menggunakan Method `QueryRow()`
-Untuk query yang menghasilkan 1 baris record saja, bisa gunakan method `QueryRow()`, dengan metode ini kode menjadi lebih ringkas. Chain dengan method `Scan()` untuk mendapatkan value-nya.
+Untuk query yang menghasilkan 1 baris record saja, bisa gunakan method `QueryRow()`. Penggunaannya membuat kode menjadi lebih ringkas. Chain dengan method `Scan()` untuk mendapatkan value yang dicari sesuai query.
```go
func sqlQueryRow() {
@@ -209,7 +213,7 @@ func main() {
}
```
-Dari kode di atas ada statement yang dituliskan cukup unik, chain statement boleh dituliskan dalam beberapa baris, contohnya:
+Di kode di atas bisa dilihat statement chain method ditulis multi-baris. Hal seperti ini diperbolehkan dengan catatan tanda titik untuk pengaksesan method berikutnya harus selalu di tuliskan di akhir baris.
```go
err = db.
@@ -266,7 +270,7 @@ Method `Prepare()` digunakan untuk deklarasi query, yang mengembalikan objek ber
## A.56.6. Insert, Update, & Delete Data Menggunakan `Exec()`
-Untuk operasi **insert**, **update**, dan **delete**; dianjurkan untuk tidak menggunakan fungsi `sql.Query()` ataupun `sql.QueryRow()` untuk eksekusinya. Direkomendasikan eksekusi perintah-perintah tersebut lewat fungsi `Exec()`, contohnya seperti pada kode berikut.
+Untuk operasi **insert**, **update**, dan **delete**; dianjurkan untuk tidak menggunakan fungsi `sql.Query()` ataupun `sql.QueryRow()` untuk eksekusinya. Gunakan fungsi `Exec()`, contoh penerapannya bisa dilihat di bawah ini:
```go
func sqlExec() {
@@ -329,7 +333,7 @@ Sebagai contoh saya menggunakan driver [pq](https://github.com/lib/pq) untuk kon
sql.Open("postgres", "user=postgres password=secret dbname=test sslmode=disable")
```
-Selengkapya mengenai driver yang tersedia bisa dilihat di [https://github.com/golang/go/wiki/SQLDrivers](https://github.com/golang/go/wiki/SQLDrivers).
+Selengkapya mengenai driver yang tersedia di Go silakan lihat di [https://go.dev/wiki/SQLDrivers](https://go.dev/wiki/SQLDrivers).
---
diff --git a/content/A-string-format.md b/content/A-string-format.md
index 2ca787e1d..5e0cea832 100644
--- a/content/A-string-format.md
+++ b/content/A-string-format.md
@@ -1,6 +1,6 @@
# A.38. Layout Format String
-Pada pembahasan-pembahasan sebelumnya kita telah banyak menggunakan layout format string seperti `%s`, `%d`, `%.2f`, dan lainnya; untuk keperluan menampilkan output ke layar ataupun untuk memformat string.
+Pada pembahasan-pembahasan sebelumnya kita telah banyak memanfaatkan layout format string, contohnya seperti `%s`, `%d`, `%.2f`, dan lainnya. Layout format string tersebut digunakan untuk keperluan formatting string untuk dimunculkan ke layar ataupun untuk disimpan ke variabel.
Layout format string digunakan dalam konversi data ke bentuk string. Contohnya seperti `%.3f` yang untuk konversi nilai `double` ke `string` dengan 3 digit desimal.
diff --git a/content/A-strings.md b/content/A-strings.md
index 1ca479fae..7419ae2d0 100644
--- a/content/A-strings.md
+++ b/content/A-strings.md
@@ -1,6 +1,6 @@
# A.44. Fungsi String
-Go menyediakan package `strings`, isinya banyak fungsi untuk keperluan pengolahan data string. Chapter ini berisi pembahasan mengenai beberapa fungsi yang ada di dalam package tersebut.
+Go menyediakan package `strings`, isinya banyak fungsi untuk keperluan pengolahan data string. Chapter ini berisi pembahasan mengenai penggunaan fungsi yang ada di dalam package tersebut.
## A.44.1. Fungsi `strings.Contains()`
diff --git a/content/A-struct.md b/content/A-struct.md
index 7dc0d1bff..f4f757dc7 100644
--- a/content/A-struct.md
+++ b/content/A-struct.md
@@ -1,18 +1,18 @@
# A.24. Struct
-Go tidak memiliki class yang ada di bahasa-bahasa strict OOP lain. Tapi Go memiliki tipe data struktur yang disebut dengan Struct.
+Go tidak mengadopsi konsep class seperti pada beberapa bahasa pemrograman OOP lainnya. Namun Go memiliki tipe data struktur Struct.
Struct adalah kumpulan definisi variabel (atau property) dan atau fungsi (atau method), yang dibungkus sebagai tipe data baru dengan nama tertentu. Property dalam struct, tipe datanya bisa bervariasi. Mirip seperti `map`, hanya saja key-nya sudah didefinisikan di awal, dan tipe data tiap itemnya bisa berbeda.
-Dari sebuah struct, kita bisa buat variabel baru, yang memiliki atribut sesuai skema struct tersebut. Kita sepakati dalam buku ini, variabel tersebut dipanggil dengan istilah **object** atau **object struct**.
+Dari sebuah struct, kita bisa buat variabel baru, yang memiliki atribut sesuai skema struct tersebut. Kita sepakati dalam buku ini, variabel tersebut dipanggil dengan istilah **object** atau **variabel object**.
-> Konsep struct di golang mirip dengan konsep **class** pada OOP, meski sebenarnya berbeda. Di sini penulis menggunakan konsep OOP sebagai analogi, dengan tujuan untuk mempermudah dalam mencerna isi chapter ini.
+> Konsep struct di golang mirip dengan konsep **class** pada OOP, meski sebenarnya memiliki perbedaan. Di sini penulis menggunakan konsep OOP sebagai analogi, untuk mempermudah pembaca untuk memahami pembelajaran di chapter ini.
-Dengan memanfaatkan struct, grouping data akan lebih mudah, selain itu rapi dan gampang untuk di-maintain.
+Dengan memanfaatkan struct, penyimpanan data yang sifatnya kolektif menjadi lebih mudah, lebih rapi, dan mudah untuk dikelola.
## A.24.1. Deklarasi Struct
-Keyword `type` digunakan untuk deklarasi struct. Di bawah ini merupakan contoh cara penggunaannya.
+Kombinasi keyword `type` dan `struct` digunakan untuk deklarasi struct. Di bawah ini merupakan contoh cara penerapannya.
```go
type student struct {
@@ -21,11 +21,11 @@ type student struct {
}
```
-Struct `student` dideklarasikan memiliki 2 property, yaitu `name` dan `grade`. Objek yang dibuat dengan struct ini nantinya memiliki skema atau struktur yang sama.
+Struct `student` dideklarasikan memiliki 2 property, yaitu `name` dan `grade`. Property adalah istilah untuk variabel yang menempel ke struct.
-## A.24.2. Penerapan Struct
+## A.24.2. Penerapan Struct Untuk Membuat Object
-Struct `student` yang sudah disiapkan di atas akan kita manfaatkan untuk membuat variabel objek. Property variabel tersebut di-isi kemudian ditampilkan.
+Struct `student` yang sudah disiapkan di atas kita gunakan untuk membuat variabel objek. Variabel tersebut tipe datanya adalah `student`. Kemudian dari variabel object, kita bisa mengakses isi property variabel. Contoh:
```go
func main() {
@@ -40,7 +40,7 @@ func main() {
Cara membuat variabel objek sama seperti pembuatan variabel biasa. Tinggal tulis saja nama variabel diikuti nama struct, contoh: `var s1 student`.
-Semua property variabel objek pada awalnya memiliki zero value sesuai tipe datanya.
+Semua property variabel objek pada awalnya memiliki zero value sesuai tipe datanya. Misalnya, 0 untuk tipe `int`, dan string kosong `""` untuk string.
Property variabel objek bisa diakses nilainya menggunakan notasi titik, contohnya `s1.name`. Nilai property-nya juga bisa diubah, contohnya `s1.grade = 2`.
@@ -48,7 +48,7 @@ Property variabel objek bisa diakses nilainya menggunakan notasi titik, contohny
## A.24.3. Inisialisasi Object Struct
-Cara inisialisasi variabel objek adalah dengan menambahkan kurung kurawal setelah nama struct. Nilai masing-masing property bisa diisi pada saat inisialisasi.
+Cara inisialisasi variabel objek adalah dengan menuliskan nama struct yang telah dibuat diikuti dengan kurung kurawal. Nilai masing-masing property bisa diisi pada saat inisialisasi.
Pada contoh berikut, terdapat 3 buah variabel objek yang dideklarasikan dengan cara berbeda.
@@ -136,11 +136,11 @@ Pada kode di atas, disiapkan struct `person` dengan properti yang tersedia adala
Embedded struct adalah **mutable**, nilai property-nya nya bisa diubah.
-Khusus untuk properti yang bukan properti asli (properti turunan dari struct lain), bisa diakses dengan cara mengakses struct *parent*-nya terlebih dahulu, contohnya `s1.person.age`. Nilai yang dikembalikan memiliki referensi yang sama dengan `s1.age`.
+Khusus untuk properti yang bukan merupakan properti asli (melainkan properti turunan dari struct lain), pengaksesannya dilakukan dengan cara mengakses struct *parent*-nya terlebih dahulu, contohnya `s1.person.age`. Nilai yang dikembalikan memiliki referensi yang sama dengan `s1.age`.
## A.24.6. Embedded Struct Dengan Nama Property Yang Sama
-Jika salah satu nama properti sebuah struct memiliki kesamaan dengan properti milik struct lain yang di-embed, maka pengaksesan property-nya harus dilakukan secara eksplisit atau jelas. Contoh bisa dilihat di kode berikut.
+Jika salah satu nama properti sebuah struct memiliki kesamaan dengan properti milik struct lain yang di-embed, maka pengaksesan property-nya harus dilakukan secara eksplisit atau jelas. Silakan lihat kode berikut agar lebih jelas.
```go
package main
@@ -189,7 +189,7 @@ Pada deklarasi `s1`, property `person` diisi variabel objek `p1`.
## A.24.8. Anonymous Struct
-Anonymous struct adalah struct yang tidak dideklarasikan di awal sebagai tipe data baru, melainkan langsung ketika pembuatan objek. Teknik ini cukup efisien untuk pembuatan variabel objek yang struct-nya hanya dipakai sekali.
+Anonymous struct adalah struct yang tidak dideklarasikan di awal sebagai tipe data baru, melainkan langsung ketika pembuatan objek. Teknik ini cukup efisien digunakan pada *use case* pembuatan variabel objek yang struct-nya hanya dipakai sekali.
```go
package main
@@ -292,7 +292,7 @@ student.grade = 2
Statement `type student struct` adalah contoh cara deklarasi struct. Maknanya akan berbeda ketika keyword `type` diganti `var`, seperti pada contoh di atas `var student struct`, yang artinya dicetak sebuah objek dari anonymous struct kemudian disimpan pada variabel bernama `student`.
-Deklarasi anonymous struct menggunakan metode ini juga bisa dilakukan beserta inisialisasi-nya.
+Deklarasi anonymous struct menggunakan metode ini juga bisa dilakukan dengan disertai inisialisasi data.
```go
// hanya deklarasi
@@ -323,7 +323,7 @@ type student struct {
}
```
-Teknik ini biasa digunakan ketika decoding data **json** yang struktur datanya cukup kompleks dengan proses decode hanya sekali.
+Teknik ini biasa digunakan ketika decoding data **JSON** yang struktur datanya cukup kompleks dengan proses decode hanya sekali.
## A.24.13. Deklarasi Dan Inisialisasi Struct Secara Horizontal
@@ -340,11 +340,9 @@ var p1 = struct { name string; age int } { age: 22, name: "wick" }
var p2 = struct { name string; age int } { "ethan", 23 }
```
-Bagi pengguna editor Sublime yang terinstal plugin GoSublime di dalamnya, cara ini tidak akan bisa dilakukan, karena setiap kali file di-save, kode program dirapikan. Jadi untuk mengetesnya bisa dengan menggunakan editor lain.
-
## A.24.14. Tag property dalam struct
-Tag merupakan informasi opsional yang bisa ditambahkan pada masing-masing property struct.
+Tag merupakan informasi opsional yang bisa ditambahkan pada property struct.
```go
type person struct {
@@ -353,7 +351,7 @@ type person struct {
}
```
-Tag biasa dimanfaatkan untuk keperluan encode/decode data json. Informasi tag juga bisa diakses lewat reflect. Nantinya akan ada pembahasan yang lebih detail mengenai pemanfaatan tag dalam struct, terutama ketika sudah masuk chapter JSON.
+Tag biasa dimanfaatkan untuk keperluan encode/decode data. Informasi tag juga bisa diakses lewat reflect. Nantinya akan ada pembahasan yang lebih detail mengenai pemanfaatan tag dalam struct, terutama ketika sudah masuk chapter JSON.
## A.24.15. Type Alias
@@ -398,9 +396,9 @@ type People2 = struct {
}
```
-Struct `People1` dideklarasikan. Struct alias `People2` juga dideklarasikan, struct ini merupakan alias dari anonymous struct. Penggunaan teknik type alias untuk anonymous struct menghasilkan output yang ekuivalen dengan pendeklarasian struct.
+Struct `People1` dideklarasikan, kemudian struct alias `People2` juga dideklarasikan. Struct `People2` merupakan alias dari anonymous struct. Penggunaan teknik type alias untuk anonymous struct menghasilkan output yang ekuivalen dengan pendeklarasian struct.
-Perlu diketahui juga, dan di atas sudah sempat disinggung, bahwa teknik type alias ini tidak dirancang hanya untuk pembuatan alias pada tipe struct saja, semua jenis tipe data bisa dibuatkan alias. Di contoh berikut, dipersiapkan tipe `Number` yang merupakan alias dari tipe data `int`.
+Teknik type alias ini tidak dirancang hanya untuk pembuatan alias pada tipe struct saja, semua jenis tipe data bisa dibuatkan alias. Contohnya seperti pada kode berikut, ada tipe data baru bernama `Number` yang merupakan alias dari tipe data `int`.
```go
type Number = int
diff --git a/content/A-time-duration.md b/content/A-time-duration.md
index 03be4e1cf..354be56c9 100644
--- a/content/A-time-duration.md
+++ b/content/A-time-duration.md
@@ -1,10 +1,10 @@
# A.42. Time Duration
-Pada chapter ini kita akan belajar tentang tipe waktu durasi yaitu `time.Duration`.
+Pada chapter ini kita akan belajar tentang tipe data untuk pengolahan durasi waktu yaitu `time.Duration`.
Tipe `time.Duration` ini merepresentasikan durasi, contohnya seperti 1 menit, 2 jam 5 detik, dst. Data dengan tipe ini bisa dihasilkan dari operasi pencarian delta atau selisih dari dua buah objek `time.Time`, atau bisa juga kita buat sendiri.
-Tipe durasi ini sangat berguna untuk banyak hal, seperti *benchmarking* durasi ataupun operasi-operasi lainnya yang membutuhkan informasi durasi.
+Tipe ini sangat berguna untuk banyak hal, salah satunya untuk *benchmarking* ataupun operasi-operasi lainnya yang membutuhkan informasi durasi waktu.
## A.42.1. Praktek
@@ -48,7 +48,7 @@ Dari list di atas bisa dicontohkan bahwa sebuah data dengan tipe `time.Duration`
Kembali ke pembahasan fungsi `time.Sleep()`, fungsi ini membutuhkan argumen/parameter durasi dalam bentuk `time.Duration`. Misalnya saya tulis `time.Sleep(1)` maka yang terjadi adalah, waktu statement tersebut hanya akan menghentikan proses selama **1 nanosecond** saja. Jika ingin menghentikan selama 1 detik, maka harus ditulis `time.Sleep(1000000000)`. Nah daripada menulis angka sepanjang itu, cukup saja tulis dengan `1 * time.Second`, artinya adalah 1 detik. Cukup mudah bukan.
-Di atas kita gunakan `5 * time.Second` sebagai argumen `time.Sleep()`, maka dengan itu proses akan diberhentikan selama 5 detik.
+Di atas, kita gunakan `5 * time.Second` sebagai argumen `time.Sleep()`, maka dengan itu proses akan diberhentikan selama 5 detik.
Sekarang jalankan program yang sudah dibuat.
@@ -56,7 +56,7 @@ Sekarang jalankan program yang sudah dibuat.
Bisa dilihat, hasilnya adalah semua statement di bawah `time.Sleep()` dieksekusi setelah 5 detik berlalu. Ini merupakan contoh penggunaan tipe data durasi pada fungsi `time.Sleep()`.
-## A.42.2. Kalkulasi Durasi Menggunakan `time.Since()`.
+## A.42.2. Hitung Durasi Menggunakan `time.Since()`.
Pada kode di atas, variabel `duration` berisi durasi atau lama waktu antara kapan variabel `start` di-inisialisasi hingga kapan variabel `duration` ini statement-nya dieksekusi.
@@ -64,7 +64,7 @@ Cara menghitung durasi bisa menggunakan `time.Since()`. Isi argumen fungsi terse
Pada contoh di atas, karena ada statement `time.Sleep(5 * time.Second)` maka idealnya `time.Since(start)` isinya adalah 5 detik (mungkin lebih sedikit, sekian mili/micro/nano-second, karena eksekusi statement juga butuh waktu).
-## A.42.3. Method `time.Duration`
+## A.42.3. Method milik tipe `time.Duration`
Tipe `time.Duration` memiliki beberapa method yang sangat-sangat berguna untuk keperluan mengambil nilai durasinya dalam unit tertentu. Misalnya, objek durasi tersebut ingin di-ambil nilainya dalam satuan unit detik, maka gunakan `.Seconds()`. Jika ingin dalam bentuk menit, maka gunakan `.Minutes()`, dan lainnya.
@@ -102,11 +102,10 @@ Kita bisa mengalikan angka literal dengan konstanta `time.Duration` untuk mencip
233 * time.Nanosecond // 233 nano detik
```
-Sedikit kembali ke pembahasan dasar di awal-awal chapter, operasi aritmatika di golang hanya bisa dilakukan ketika data adalah 1 tipe. Selebihnya harus ada casting atau konversi tipe data agar bisa dioperasikan.
+Mengulas kembali pembahasan dasar di awal-awal chapter, operasi aritmatika di golang hanya bisa dilakukan ketika data adalah 1 tipe. Selebihnya harus ada casting atau konversi tipe data agar bisa dioperasikan.
Tipe `time.Duration` diciptakan menggunakan tipe `ìnt64`. Jadi jika ingin mengalikan `time.Duration` dengan suatu angka, maka pastikan tipe-nya juga sama yaitu `time.Duration`. Jika angka tersebut tidak ditampung dalam variabel terlebih dahulu (contohnya seperti di atas) maka bisa langsung kalikan saja. Jika ditampung ke variabel terlebih dahulu, maka pastikan tipe variabelnya adalah `time.Duration`. Contoh:
-
```go
var n time.Duration = 5
duration := n * time.Second
diff --git a/content/A-time-parsing-format.md b/content/A-time-parsing-format.md
index c70d10d34..8eb4070e1 100644
--- a/content/A-time-parsing-format.md
+++ b/content/A-time-parsing-format.md
@@ -1,10 +1,10 @@
# A.40. Time, Parsing Time, & Format Time
-Pada chapter ini kita akan belajar tentang pemanfaatan data bertipe date-time, method-method yang disediakan, dan juga **format** & **parsing** data `string` ke tipe `time.Time` dan sebaliknya.
+Pada chapter ini kita akan belajar tentang pemanfaatan data bertipe datetime serta method-method-nya, juga tentang **format** & **parsing** data `string` ke tipe `time.Time` dan sebaliknya.
-Go menyediakan package `time` yang berisikan banyak sekali komponen yang bisa digunakan untuk keperluan pemanfaatan date-time. Salah satunya adalah `time.Time`, yang merupakan tipe untuk data tanggal dan waktu di Go.
+Go menyediakan package `time` yang berisikan banyak sekali komponen yang bisa digunakan untuk keperluan pemanfaatan date dan time. Salah satunya adalah `time.Time`, yang merupakan tipe untuk data tanggal dan waktu di Go.
-> Time di sini maksudnya adalah gabungan **date** dan **time**, bukan hanya waktu saja.
+> Meskipun nama package-nya adalah `time`, yang dicakup adalah **date** dan **time**, jadi bukan hanya waktu saja.
## A.40.1. Penggunaan `time.Time`
@@ -32,7 +32,7 @@ func main() {
}
```
-Fungsi `time.Now()` mengembalikan objek `time.Time` dengan informasi adalah date-time tepat ketika statement tersebut dijalankan. Bisa dilihat pada saat di-print muncul informasi date-time sesuai dengan tanggal program tersebut dieksekusi.
+Fungsi `time.Now()` mengembalikan objek `time.Time` dengan nilai adalah informasi date-time tepat ketika statement tersebut dijalankan. Bisa dilihat pada saat di-print muncul informasi date-time sesuai dengan tanggal program tersebut dieksekusi.
![Penggunaan time](images/A_time_parsing_format_1_time_instance.png)
@@ -42,7 +42,7 @@ Fungsi `time.Date()` digunakan untuk membuat objek `time.Time` baru yang informa
time.Date(tahun, bulan, tanggal, jam, menit, detik, nanodetik, timezone)
```
-Objek cetakan fungsi `time.Now()`, informasi timezone-nya adalah relatif terhadap lokasi kita. Karena kebetulan penulis berlokasi di Jawa Timur, maka akan terdeteksi masuk dalam **GMT+7** atau **WIB**. Berbeda dengan variabel `time2` yang lokasinya sudah kita tentukan secara eksplisit yaitu **UTC**.
+Objek cetakan fungsi `time.Now()` memiliki timezone yang relatif terhadap lokasi kita. Karena kebetulan penulis berlokasi di Jawa Timur, maka akan terdeteksi masuk dalam **GMT+7** atau **WIB**. Berbeda dengan variabel `time2` yang lokasinya sudah kita tentukan secara eksplisit yaitu **UTC**.
Selain menggunakan `time.UTC` untuk penentuan lokasi, tersedia juga `time.Local` yang nilainya adalah relatif terhadap date-time lokal kita.
@@ -194,7 +194,7 @@ Variabel `date` di atas berisikan hasil parsing data dengan format `time.RFC822`
## A.40.6. Handle Error Parsing `time.Time`
-Ketika parsing `string` ke `time.Time`, sangat memungkinkan bisa terjadi error karena struktur data yang akan di-parse tidak sesuai layout format yang digunakan. Error tidaknya parsing bisa diketahui lewat nilai kembalian ke-2 fungsi `time.Parse()`. Berikut adalah contoh penerapannya.
+Parsing `string` ke `time.Time` memungkinkan terjadinya error, misalnya karena struktur data yang akan di-parse tidak sesuai layout format yang digunakan. Error-tidaknya parsing bisa diketahui lewat nilai kembalian ke-2 fungsi `time.Parse()`. Contoh:
```go
var date, err = time.Parse("06 Jan 15", "02 Sep 15 08:00 WIB")
diff --git a/content/A-timer-ticker-scheduler.md b/content/A-timer-ticker-scheduler.md
index 502717b9a..ddcce0eb7 100644
--- a/content/A-timer-ticker-scheduler.md
+++ b/content/A-timer-ticker-scheduler.md
@@ -1,6 +1,6 @@
# A.41. Timer, Ticker, & Scheduler
-Ada beberapa fungsi dalam package `time` yang bisa dimanfaatkan untuk menunda atau mengatur jadwal eksekusi sebuah proses dalam jeda waktu tertentu.
+Ada beberapa fungsi dalam package `time` yang bisa dimanfaatkan untuk operasi penundaan eksekusi, countdown timer, dan pengaturan jadwal eksekusi sebuah proses.
## A.41.1. Fungsi `time.Sleep()`
@@ -72,7 +72,7 @@ Hasil dari kode di atas, tulisan `"start"` muncul kemudian setelah 4 detik berla
Di dalam callback terdapat proses transfer data lewat channel, menjadikan tulisan `"finish"` akan muncul tepat setelah tulisan `"expired"` muncul.
-Beberapa hal yang perlu diketahui dalam menggunakan fungsi ini:
+Beberapa hal yang perlu diketahui ketika menggunakan fungsi ini:
- Jika tidak ada serah terima data lewat channel, maka eksekusi `time.AfterFunc()` adalah asynchronous (tidak blocking).
- Jika ada serah terima data lewat channel, maka fungsi akan tetap berjalan asynchronous hingga baris kode di mana penerimaan data channel dilakukan. Proses blocking nya berada pada baris kode penerimaan channel.
@@ -126,7 +126,7 @@ func main() {
Pada contoh di atas bisa dilihat, selain ticker disiapkan juga variabel channel `done`. Variabel ini kita gunakan untuk mengontrol kapan ticker harus di stop.
-Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap N duration yang di mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker).
+Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang di mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker).
Data yang dikirimkan via channel `ticker.C` adalah data date-time kapan event itu terjadi. Pada kode di atas, setiap ada kiriman data via channel tersebut kita tampilkan.
@@ -190,7 +190,7 @@ func main() {
}
```
-Ketika user tidak menginputkan apa-apa dalam kurun waktu 5 detik, maka akan muncul pesan timeout, lalu program dihentikan.
+Ketika user tidak menginputkan apa-apa dalam kurun waktu 5 detik, pesan timeout muncul lalu program berhenti.
![Penerapan timer dalam goroutine](images/A_timer_ticker_scheduler_1_timer.png)
diff --git a/content/A-tipe-data.md b/content/A-tipe-data.md
index e89d0d574..9e72bfaaa 100644
--- a/content/A-tipe-data.md
+++ b/content/A-tipe-data.md
@@ -2,9 +2,9 @@
Go mengenal beberapa jenis tipe data, di antaranya adalah tipe data numerik (desimal & non-desimal), string, dan boolean.
-Pada pembahasan-pembahasan sebelumnya secara tak sadar kita sudah mengaplikasikan beberapa tipe data, seperti `string` dan tipe numerik `int`.
+Pada pembahasan-pembahasan sebelumnya secara tak sadar kita sudah mengaplikasikan beberapa tipe data, diantaranya ada `string` dan tipe numerik `int`.
-Pada chapter ini akan dijelaskan beberapa macam tipe data standar yang disediakan oleh Go, beserta cara penggunaannya.
+Pada chapter ini akan dijelaskan beberapa macam tipe data standar yang disediakan oleh Go, disertai juga contohnya.
## A.10.1. Tipe Data Numerik Non-Desimal
@@ -42,7 +42,7 @@ fmt.Printf("bilangan negatif: %d\n", negativeNumber)
Variabel `positiveNumber` bertipe `uint8` dengan nilai awal `89`. Sedangkan variabel `negativeNumber` dideklarasikan dengan nilai awal `-1243423644`. Compiler secara cerdas akan menentukan tipe data variabel tersebut sebagai `int32` (karena angka tersebut masuk ke cakupan tipe data `int32`).
-Template `%d` pada `fmt.Printf()` digunakan untuk memformat data numerik non-desimal.
+String format `%d` pada `fmt.Printf()` digunakan untuk memformat data numerik non-desimal.
## A.10.2. Tipe Data Numerik Desimal
@@ -59,7 +59,7 @@ Pada kode di atas, variabel `decimalNumber` akan memiliki tipe data `float32`, k
![Tipe data numerik desimal](images/A_tipe_data_1_decimal_data_type.png)
-Template `%f` digunakan untuk memformat data numerik desimal menjadi string. Digit desimal yang akan dihasilkan adalah **6 digit**. Pada contoh di atas, hasil format variabel `decimalNumber` adalah `2.620000`. Jumlah digit yang muncul bisa dikontrol menggunakan `%.nf`, tinggal ganti `n` dengan angka yang diinginkan. Contoh: `%.3f` maka akan menghasilkan 3 digit desimal, `%.10f` maka akan menghasilkan 10 digit desimal.
+String format `%f` digunakan untuk memformat data numerik desimal menjadi string. Digit desimal yang akan dihasilkan adalah **6 digit**. Pada contoh di atas, hasil format variabel `decimalNumber` adalah `2.620000`. Jumlah digit yang muncul bisa dikontrol menggunakan `%.nf`, tinggal ganti `n` dengan angka yang diinginkan. Contoh: `%.3f` maka akan menghasilkan 3 digit desimal, `%.10f` maka akan menghasilkan 10 digit desimal.
## A.10.3. Tipe Data `bool` (Boolean)
@@ -106,7 +106,9 @@ Semua tipe data yang sudah dibahas di atas memiliki zero value (nilai default ti
- Zero value dari tipe numerik non-desimal adalah `0`.
- Zero value dari tipe numerik desimal adalah `0.0`.
-Zero value berbeda dengan `nil`. `Nil` adalah nilai kosong, benar-benar kosong. `nil` tidak bisa digunakan pada tipe data yang sudah dibahas di atas. Ada beberapa tipe data yang bisa di-set nilainya dengan `nil`, di antaranya:
+Selain tipe data yang disebutkan di atas, ada juga tipe data lain yang zero value-nya adalah `nil`. `Nil` merepresentasikan nilai kosong, benar-benar kosong. `nil` tidak bisa digunakan pada tipe data yang sudah dibahas di atas.
+
+Beberapa tipe data yang bisa di-set nilainya dengan `nil`, di antaranya:
- pointer
- tipe data fungsi
@@ -115,7 +117,7 @@ Zero value berbeda dengan `nil`. `Nil` adalah nilai kosong, benar-benar kosong.
- `channel`
- interface kosong atau `any` (yang merupakan alias dari `interface{}`)
-Nantinya kita akan sering bertemu dengan `nil` setelah masuk pada pembahasan-pembahasan tersebut.
+Nantinya kita akan sering bertemu dengan nilai `nil` setelah masuk pada pembahasan-pembahasan tersebut.
---
diff --git a/content/A-unit-test.md b/content/A-unit-test.md
index fcaa0174e..8e427229a 100644
--- a/content/A-unit-test.md
+++ b/content/A-unit-test.md
@@ -1,8 +1,8 @@
# A.58. Unit Test
-Go menyediakan package `testing`, berisikan banyak sekali tools untuk keperluan unit test.
+Go menyediakan package `testing` yang isinya banyak sekali API untuk keperluan pembuatan test file.
-Pada chapter ini kita akan belajar mengenai testing, benchmark, dan juga testing menggunakan [testify](https://github.com/stretchr/testify).
+Pada chapter ini kita akan belajar mengenai testing, benchmark, dan juga testing menggunakan *3rd party* [testify](https://github.com/stretchr/testify).
## A.58.1. Persiapan
@@ -32,9 +32,9 @@ func (k Kubus) Keliling() float64 {
Simpan kode di atas dengan nama `bab55.go`.
-## A.58.2. Testing
+## A.58.2. File Testing
-File untuk keperluan testing dipisah dengan file utama, namanya harus berakhiran `_test.go`, dan package-nya harus sama. Pada chapter ini, file utama adalah `bab55.go`, maka file testing harus bernama `bab55_test.go`.
+Di Go, file untuk keperluan testing dipisah dengan file utama. Nama file testing harus berakhiran `_test.go`, dan harus ditempatkan di package yang sama seperti source code yang akan di-test. Pada chapter ini, file utama adalah `bab55.go`, maka file testing harus bernama `bab55_test.go`.
Unit test di Go dituliskan dalam bentuk fungsi, yang memiliki parameter yang bertipe `*testing.T`, dengan nama fungsi harus diawali kata **Test** (pastikan sudah meng-import package `testing` sebelumnya). Lewat parameter tersebut, kita bisa mengakses method-method untuk keperluan testing.
@@ -144,21 +144,19 @@ Arti dari `30000000 51.1 ns/op` adalah, fungsi di atas di-test sebanyak **30 ju
## A.58.5. Testing Menggunakan testify
-Package **testify** berisikan banyak sekali tools yang bisa dimanfaatkan untuk keperluan testing di Go.
+Package **testify** merupakan salah satu dari sekian banyak *3rd party* yang tersedia untuk keperluan testing di Go. Testify bisa di-download di [github.com/stretchr/testify](https://github.com/stretchr/testify) menggunakan perintah `go get`.
-Testify bisa di-download pada [github.com/stretchr/testify](https://github.com/stretchr/testify) menggunakan `go get`.
-
-Di dalam testify terdapat 5 package dengan kegunaan berbeda-beda satu dengan lainnya. Detailnya bisa dilihat pada tabel berikut.
+Dalam testify terdapat 5 package yang masing-masing memiliki kegunaan berbeda-beda satu sama lain. Detailnya bisa dilihat pada tabel berikut.
| Package | Kegunaan |
| :------ | :------- |
-| assert | Berisikan tools standar untuk testing |
-| http | Berisikan tools untuk keperluan testing http |
-| mock | Berisikan tools untuk mocking object |
-| require | Sama seperti assert, hanya saja jika terjadi fail pada saat test akan menghentikan eksekusi program |
-| suite | Berisikan tools testing yang berhubungan dengan struct dan method |
+| `assert` | Berisikan tools standar untuk testing |
+| `http` | Berisikan tools untuk keperluan testing http |
+| `mock` | Berisikan tools untuk mocking object |
+| `require` | Sama seperti assert, hanya saja jika terjadi fail pada saat test akan menghentikan eksekusi program |
+| `suite` | Berisikan tools testing yang berhubungan dengan struct dan method |
-Pada chapter ini akan kita contohkan bagaimana penggunaan package assert. Silakan perhatikan contoh berikut.
+Pada chapter ini akan kita contohkan bagaimana penggunaan package `assert`. Silakan perhatikan contoh berikut.
```go
import "github.com/stretchr/testify/assert"
diff --git a/content/A-url-parsing.md b/content/A-url-parsing.md
index bade0106e..a12e46917 100644
--- a/content/A-url-parsing.md
+++ b/content/A-url-parsing.md
@@ -1,6 +1,6 @@
# A.52. URL Parsing
-Data string url bisa dikonversi ke dalam bentuk `url.URL`. Dengan menggunakan tipe tersebut akan ada banyak informasi yang bisa kita manfaatkan, di antaranya adalah jenis protokol yang digunakan, path yang diakses, query, dan lainnya.
+Data string berisi informasi URL bisa dikonversi ke tipe data `url.URL`. Dengan menggunakan tipe `url.URL`, akan ada banyak informasi yang bisa kita dapatkan dengan mudah, di antaranya seperti jenis protokol yang digunakan, path yang diakses, query, dan lainnya.
Berikut adalah contoh sederhana konversi string ke `url.URL`.
@@ -30,7 +30,7 @@ func main() {
}
```
-Fungsi `url.Parse()` digunakan untuk parsing string ke bentuk url. Mengembalikan 2 data, variabel objek bertipe `url.URL` dan error (jika ada). Lewat variabel objek tersebut pengaksesan informasi url akan menjadi lebih mudah, contohnya seperti nama host bisa didapatkan lewat `u.Host`, protokol lewat `u.Scheme`, dan lainnya.
+Fungsi `url.Parse()` digunakan untuk parsing string ke bentuk url. Fungsi ini mengembalikan 2 data, variabel objek bertipe `url.URL` dan error (jika ada). Lewat variabel objek tersebut pengaksesan informasi url akan menjadi lebih mudah, contohnya seperti nama host bisa didapatkan lewat `u.Host`, protokol lewat `u.Scheme`, dan lainnya.
Selain itu, query yang ada pada url akan otomatis diparsing juga, menjadi bentuk `map[string][]string`, dengan key adalah nama elemen query, dan value array string yang berisikan value elemen query.
diff --git a/content/A-variabel.md b/content/A-variabel.md
index 39e017488..77d2fd87d 100644
--- a/content/A-variabel.md
+++ b/content/A-variabel.md
@@ -1,14 +1,14 @@
# A.9. Variabel
-Go mengadopsi dua jenis penulisan variabel, yaitu yang dituliskan tipe data-nya, dan juga yang tidak. Kedua cara tersebut valid dan tujuannya sama, pembedanya hanya cara penulisannya saja.
+Go mengadopsi dua jenis penulisan variabel, yaitu yang dituliskan tipe data-nya dan yang tidak. Kedua cara tersebut valid dan tujuannya sama yaitu untuk deklarasi variabel, pembedanya hanya pada cara penulisannya saja.
Pada chapter ini akan dikupas tuntas tentang macam-macam cara deklarasi variabel.
## A.9.1. Deklarasi Variabel Beserta Tipe Data
-Go memiliki aturan cukup ketat dalam hal penulisan variabel. Ketika deklarasi, tipe data yg digunakan harus dituliskan juga. Istilah lain dari konsep ini adalah **manifest typing**.
+Go memiliki aturan cukup ketat dalam hal penulisan variabel. Ketika deklarasi, tipe data yg digunakan harus dituliskan juga. Istilah dari metode deklarasi variabel ini adalah **manifest typing**.
-Berikut adalah contoh cara pembuatan variabel yang tipe datanya harus ditulis. Silakan tulis pada project baru atau pada project yang sudah ada, bebas. Pastikan saja untuk setiap project baru untuk tidak lupa inisialisasi project menggunakan command `go mod init `. Ok lanjut.
+Berikut adalah contoh cara pembuatan variabel yang tipe datanya harus ditulis. Silakan tulis pada project baru atau pada project yang sudah ada, bebas. Pastikan pada setiap pembuatan project baru untuk tidak lupa menginisialisasi project menggunakan command `go mod init `.
```go
package main
@@ -51,7 +51,7 @@ var firstName string = "john"
Nilai variabel bisa di-isi langsung pada saat deklarasi variabel.
-#### • Penggunaan Fungsi `fmt.Printf()`
+#### ◉ Penggunaan Fungsi `fmt.Printf()`
Fungsi ini digunakan untuk menampilkan output dalam bentuk tertentu. Kegunaannya sama seperti fungsi `fmt.Println()`, hanya saja struktur outputnya didefinisikan di awal.
@@ -65,13 +65,13 @@ fmt.Printf("halo %s %s!\n", firstName, lastName)
fmt.Println("halo", firstName, lastName + "!")
```
-Tanda plus (`+`) jika ditempatkan di antara string, fungsinya adalah untuk penggabungan string atau *string concatenation*.
+Tanda plus (`+`) jika digunakan untuk penghubung 2 data string fungsinya adalah untuk operasi penggabungan string atau *string concatenation*.
Fungsi `fmt.Printf()` tidak menghasilkan baris baru di akhir text, oleh karena itu digunakanlah literal *newline* yaitu `\n`, untuk memunculkan baris baru di akhir. Hal ini sangat berbeda jika dibandingkan dengan fungsi `fmt.Println()` yang secara otomatis menghasilkan new line (baris baru) di akhir.
## A.9.3. Deklarasi Variabel Tanpa Tipe Data
-Selain *manifest typing*, Go juga mengadopsi konsep **type inference**, yaitu metode deklarasi variabel yang tipe data-nya ditentukan oleh tipe data nilainya, cara kontradiktif jika dibandingkan dengan cara pertama. Dengan metode jenis ini, keyword `var` dan tipe data tidak perlu ditulis.
+Selain *manifest typing*, Go juga mengadopsi konsep **type inference**, yaitu metode deklarasi variabel yang tipe data-nya diketahui secara otomatis dari data/nilai variabel. Cara ini kontradiktif jika dibandingkan dengan cara pertama. Dengan metode jenis ini, keyword `var` dan tipe data tidak perlu ditulis.
```go
var firstName string = "john"
@@ -94,7 +94,7 @@ var firstName = "john"
lastName := "wick"
```
-Kedua deklarasi di atas maksudnya sama. Silakan pilih yang nyaman di hati.
+Kedua deklarasi di atas maksudnya sama. Silakan pilih sesuai preferensi.
Tanda `:=` hanya digunakan sekali di awal pada saat deklarasi. Untuk assignment nilai selanjutnya harus menggunakan tanda `=`, contoh:
@@ -104,7 +104,7 @@ lastName = "ethan"
lastName = "bourne"
```
-> Perlu diketahui, deklarasi menggunakan `:=` hanya bisa dilakukan di dalam blok fungsi.
+> Deklarasi menggunakan `:=` hanya bisa dilakukan di dalam blok fungsi, misalnya dalam blok fungsi `main()`
## A.9.4. Deklarasi Multi Variabel
@@ -129,7 +129,7 @@ seventh, eight, ninth := "tujuh", "delapan", "sembilan"
Dengan menggunakan teknik type inference, deklarasi multi variabel bisa dilakukan untuk variabel-variabel yang tipe data satu sama lainnya berbeda.
-```
+```go
one, isFriday, twoPointTwo, say := 1, true, 2.2, "hello"
```
@@ -153,11 +153,11 @@ Variabel underscore adalah *predefined*, jadi tidak perlu menggunakan `:=` untuk
Biasanya variabel underscore sering dimanfaatkan untuk menampung nilai balik fungsi yang tidak digunakan.
-Perlu diketahui, bahwa isi variabel underscore tidak dapat ditampilkan. Data yang sudah masuk variabel tersebut akan hilang. Ibaratkan variabel underscore seperti blackhole, objek apapun yang masuk ke dalamnya, akan terjebak selamanya di-dalam singularity dan tidak akan bisa keluar 😁
+Perlu diketahui, bahwa isi variabel underscore tidak dapat ditampilkan. Data yang sudah masuk variabel tersebut akan hilang. Ibarat variabel underscore ini seperti blackhole, objek apapun yang masuk ke dalamnya, akan terjebak selamanya di-dalam singularity dan tidak akan bisa keluar 😁
## A.9.6. Deklarasi Variabel Menggunakan Keyword `new`
-Keyword `new` digunakan untuk membuat variabel **pointer** dengan tipe data tertentu. Nilai data default-nya akan menyesuaikan tipe datanya.
+Fungsi `new()` digunakan untuk membuat variabel **pointer** dengan tipe data tertentu. Nilai data default-nya akan menyesuaikan tipe datanya.
```go
name := new(string)
@@ -166,19 +166,19 @@ fmt.Println(name) // 0x20818a220
fmt.Println(*name) // ""
```
-Variabel `name` menampung data bertipe **pointer string**. Jika ditampilkan yang muncul bukanlah nilainya melainkan alamat memori nilai tersebut (dalam bentuk notasi heksadesimal). Untuk menampilkan nilai aslinya, variabel tersebut perlu di-**dereference** terlebih dahulu, menggunakan tanda asterisk (`*`).
+Variabel `name` menampung data bertipe **pointer string**. Jika ditampilkan yang muncul bukanlah nilainya melainkan alamat memori nilai tersebut (dalam bentuk notasi heksadesimal). Untuk menampilkan nilai aslinya, variabel tersebut perlu di-**dereference** terlebih dahulu, caranya dengan menuliskan tanda asterisk (`*`) sebelum nama variabel.
Mungkin untuk sekarang banyak yang akan bingung tentang apa itu pointer, namun tak apa, karena nantinya pada chapter [A.23. Pointer](/A-pointer.html) akan dikupas habis topik tersebut.
## A.9.7. Deklarasi Variabel Menggunakan Keyword `make`
-Keyword ini hanya bisa digunakan untuk pembuatan beberapa jenis variabel saja, yaitu:
+Fungsi `make()` ini hanya bisa digunakan untuk pembuatan beberapa jenis variabel saja, yaitu:
- channel
- slice
- map
-Dan lagi, mungkin banyak yang akan bingung. Ketika sudah masuk ke pembahasan masing-masing poin tersebut, akan terlihat apa kegunaan dari keyword `make` ini.
+Nantinya kita akan bahas lebih detail ketika sudah masuk ke pembahasan masing-masing poin tersebut.
---
diff --git a/content/A-waitgroup.md b/content/A-waitgroup.md
index e2566f5a9..608acc80a 100644
--- a/content/A-waitgroup.md
+++ b/content/A-waitgroup.md
@@ -1,16 +1,16 @@
# A.59. WaitGroup
-Sebelumnya kita telah belajar banyak mengenai channel, yang fungsi utama-nya adalah untuk sharing data antar goroutine. Selain untuk komunikasi data, channel secara tidak langsung bisa dimanfaatkan untuk kontrol goroutine.
+Sebelumnya kita telah belajar banyak mengenai channel, yang dimana fungsi utama-nya adalah untuk sharing/kirim data antar goroutine. Selain untuk komunikasi data, channel secara tidak langsung bisa dimanfaatkan untuk kontrol goroutine.
-Go menyediakan package `sync`, berisi cukup banyak API untuk handle masalah multiprocessing (goroutine), salah satunya di antaranya adalah yang kita bahas pada chapter ini, yaitu `sync.WaitGroup`.
+Go menyediakan package `sync`, berisi cukup banyak API untuk manajemen operasi multiprocessing (goroutine), salah satunya di antaranya adalah yang kita bahas pada chapter ini, yaitu `sync.WaitGroup`.
-Kegunaan `sync.WaitGroup` adalah untuk sinkronisasi goroutine. Berbeda dengan channel, `sync.WaitGroup` memang dirancang khusus untuk maintain goroutine, penggunaannya lebih mudah dan lebih efektif dibanding channel.
+Kegunaan `sync.WaitGroup` adalah untuk sinkronisasi goroutine. Berbeda dengan channel, `sync.WaitGroup` memang dirancang khusus untuk pengelolahan goroutine, dengan penggunaan relatif lebih mudah dan efektif dibanding channel.
> Sebenarnya kurang pas jika membandingkan `sync.WaitGroup` dan channel, karena fungsi utama dari keduanya adalah berbeda. Channel untuk keperluan sharing data antar goroutine, sedangkan `sync.WaitGroup` untuk sinkronisasi goroutine.
## A.59.1. Penerapan `sync.WaitGroup`
-`sync.WaitGroup` digunakan untuk menunggu goroutine. Cara penggunaannya sangat mudah, tinggal masukan jumlah goroutine yang dieksekusi, sebagai parameter method `Add()` object cetakan `sync.WaitGroup`. Dan pada akhir tiap-tiap goroutine, panggil method `Done()`. Juga, pada baris kode setelah eksekusi goroutine, panggil method `Wait()`.
+`sync.WaitGroup` digunakan untuk menunggu goroutine. Cara pengaplikasiannya sangat mudah, tinggal masukan jumlah goroutine yang dieksekusi, sebagai parameter method `Add()` pada object cetakan `sync.WaitGroup`, kemudian di akhir setiap goroutine pastikan untuk memanggil method `Done()`. Lalu gunakan method `Wait()` untuk menunggu eksekusi semua goroutine selesai.
Agar lebih jelas, silakan coba kode berikut.
@@ -44,7 +44,7 @@ func main() {
Kode di atas merupakan contoh penerapan `sync.WaitGroup` untuk pengelolahan goroutine. Fungsi `doPrint()` akan dijalankan sebagai goroutine, dengan tugas menampilkan isi variabel `message`.
-Variabel `wg` disiapkan bertipe `sync.WaitGroup`, dibuat untuk sinkronisasi goroutines yang dijalankan.
+Variabel `wg` dibuat dengan tipe data `sync.WaitGroup`. Variabel ini digunakan sebagai kontrol dan sinkronisasi goroutines yang dijalankan.
Di tiap perulangan statement `wg.Add(1)` dipanggil. Kode tersebut akan memberikan informasi kepada `wg` bahwa jumlah goroutine yang sedang di proses ditambah 1 (karena dipanggil 5 kali, maka `wg` akan sadar bahwa terdapat 5 buah goroutine sedang berjalan).
@@ -52,7 +52,7 @@ Di baris selanjutnya, fungsi `doPrint()` dieksekusi sebagai goroutine. Di dalam
Statement `wg.Wait()` bersifat blocking, proses eksekusi program tidak akan diteruskan ke baris selanjutnya, sebelum sejumlah 5 goroutine selesai. Jika `Add(1)` dipanggil 5 kali, maka `Done()` juga harus dipanggil 5 kali.
-Output program di atas.
+Output program di atas:
![Contoh penerapan `sync.WaitGroup`](images/A_waitgroup_1_waitgroup.png)
@@ -60,14 +60,14 @@ Output program di atas.
## A.59.2. Perbedaan WaitGroup Dengan Channel
-Bukan sebuah perbandingan yang valid, tapi jika dibandingkan maka perbedaan antara channel dan `sync.WaitGroup` kurang lebih sebagai berikut:
+Bukan sebuah perbandingan yang *fair*, tapi jika dilihat perbedaan antara channel dan `sync.WaitGroup` kurang lebih ada di bagian ini:
- Channel tergantung kepada goroutine tertentu dalam penggunaannya, tidak seperti `sync.WaitGroup` yang dia tidak perlu tahu goroutine mana saja yang dijalankan, cukup tahu jumlah goroutine yang harus selesai
- Penerapan `sync.WaitGroup` lebih mudah dibanding channel
- Kegunaan utama channel adalah untuk komunikasi data antar goroutine. Sifatnya yang blocking bisa kita manfaatkan untuk manage goroutine; sedangkan WaitGroup khusus digunakan untuk sinkronisasi goroutine
- Performa `sync.WaitGroup` lebih baik dibanding channel, sumber: https://groups.google.com/forum/#!topic/golang-nuts/whpCEk9yLhc
-Kombinasi yang tepat antara `sync.WaitGroup` dan channel sangat penting, keduanya diperlukan dalam concurrent process agar bisa maksimal.
+Kombinasi yang tepat antara `sync.WaitGroup` dan channel sangat penting, keduanya diperlukan dalam concurrent programming program performansinya bisa maksimal.
---
diff --git a/content/A-web-server.md b/content/A-web-server.md
index f4cc144fc..0eb5bf7cf 100644
--- a/content/A-web-server.md
+++ b/content/A-web-server.md
@@ -1,8 +1,8 @@
# A.51. Web Server
-Go menyediakan package `net/http`, berisi berbagai macam fitur untuk keperluan pembuatan aplikasi berbasis web. Termasuk di dalamnya web server, routing, templating, dan lainnya.
+Go menyediakan package `net/http`, berisi berbagai macam fitur untuk keperluan pembuatan aplikasi berbasis web. Termasuk di dalamnya mencakup web server, routing, templating, dan lainnya.
-Go memiliki web server sendiri, dan web server tersebut berada di dalam Go, tidak seperti bahasa lain yang servernya terpisah dan perlu diinstal sendiri (seperti PHP yang memerlukan Apache, .NET yang memerlukan IIS).
+Go memiliki web server sendiri, tersedia dalam package Go yang bisa kita import dengan mudah. Jadi berbeda dibanding beberapa bahasa lain yang servernya terpisah yang perlu diinstal sendiri (seperti PHP yang memerlukan Apache, .NET yang memerlukan IIS).
Pada chapter ini kita akan belajar cara pembuatan aplikasi web sederhana dan pemanfaatan template untuk mendesain view.
@@ -38,7 +38,7 @@ Jalankan program tersebut.
![Eksekusi program](images/A_web_server_1_start_server.png)
-Jika muncul dialog **Do you want the application “bab48” to accept incoming network connections?** atau sejenis, pilih allow. Setelah itu, buka url [http://localhost/](http://localhost/) dan [http://localhost/index](http://localhost/index/) lewat browser.
+Jika muncul dialog **Do you want the application “bab48” to accept incoming network connections?** atau sejenis, pilih allow. Setelah itu, buka url [http://localhost/](http://localhost/) dan [http://localhost/index](http://localhost/index) di browser.
![Contoh penerapan net/http](images/A_web_server_2_web_server.png)
@@ -108,7 +108,7 @@ func main() {
}
```
-Jalankan, lalu buka [http://localhost:8080/](http://localhost:8080/), maka data `Nama` dan `Message` akan muncul di view.
+Jalankan, lalu buka [http://localhost:8080](http://localhost:8080), maka data `Nama` dan `Message` akan muncul di view.
![Penggunaan template](images/A_web_server_3_template.png)
@@ -120,7 +120,7 @@ Pada contoh di atas, statement di view `{{.Name}}` akan menampilkan isi dari `da
## A.51.3. Advanced Web Programming
-Sampai chapter ini yang kita pelajari adalah dasar-dasar pemrograman Go, dibahas per topik adalah per chapter. Nantinya jika sudah masuk [B.1. Golang Web App: Hello World](/B-golang-web-hello-world.html) hingga seterusnya akan lebih banyak membahas mengenai pemrograman web, jadi sabar dulu. Mari kita selesaikan dulu yang fundamental sebelum masuk ke web development.
+Sampai chapter ini yang kita pelajari adalah yang sifatnya fundamental atau dasar di pemrograman Go. Nantinya di chapter [B.1. Golang Web App: Hello World](/B-golang-web-hello-world.html) hingga seterusnya akan lebih banyak membahas mengenai pemrograman web. Jadi untuk sekarang sabar dulu ya. Mari kita selesaikan pembelajaran fundamental secara runtun, sebelum masuk ke bagian pengembangan web.
---
diff --git a/content/A-web-service-api.md b/content/A-web-service-api.md
index f8f563041..e4df9a3c1 100644
--- a/content/A-web-service-api.md
+++ b/content/A-web-service-api.md
@@ -1,8 +1,8 @@
# A.54. Web Service API Server
-Pada chapter ini kita akan mengkombinasikan pembahasan 2 chapter sebelumnya, yaitu web programming dan JSON, untuk membuat sebuah web service API dengan tipe data reponse berbentuk JSON.
+Pada chapter ini kita akan mencoba mengkombinasikan hasl pembelajaran di 2 chapter sebelumnya (yaitu web programming dan JSON), untuk membuat sebuah web service API yang memilikki endpoint dengan reponse data mengadopsi format JSON.
-> Web Service API adalah sebuah web yang menerima request dari client dan menghasilkan response, biasa berupa JSON/XML.
+> Web Service API adalah sebuah web yang menerima request dari client dan menghasilkan response, biasa berupa JSON/XML atau format lainnya.
## A.54.1. Pembuatan Web API
@@ -129,15 +129,16 @@ Setelah web server sudah berjalan, web service yang telah dibuat perlu untuk di-
## A.54.3. Test Web Service API via `cURL`
-Untuk testing bisa juga memanfaatkan cURL. Apabila pembaca menggunakan Windows 10, seharusnya sudah ter-include cURL. Jika bukan pengguna Windows 10, bisa menginstall-nya dan mendaftarkannya ke path variables (agar bisa diakses melalui terminal/cmd dari mana saja).
+Testing bisa juga dilakukan via cURL. Pastikan untuk menginstall cURL terlebih dahulu agar bisa menggunakan command berikut.
+```
curl -X GET http://localhost:8080/users
curl -X GET http://localhost:8080/user?id=B002
- ```
+```
![cURL test](images/A_web_service_4.png)
-Data ID yang ingin dicari melalui endpoint /user, ditulis dengan ?id=B002 yang berarti dilewatkan melalui query parameters (umumnya data yang ingin dilampirkan melalui method GET adalah dengan query parameters).
+Data user ID pada endpoint `/user` ditulis dalam format query parameters, yaitu `?id=B002`.
---
diff --git a/content/B-golang-web-hello-world.md b/content/B-golang-web-hello-world.md
index 9f0b213df..6acbd481a 100644
--- a/content/B-golang-web-hello-world.md
+++ b/content/B-golang-web-hello-world.md
@@ -69,7 +69,7 @@ Cek pada browser rute yang sudah dibuat, output akan muncul.
Berikut merupakan penjelasan detail per-bagian program yang telah kita buat dari contoh di atas.
-#### • Penggunaan `http.HandleFunc()`
+#### ◉ Penggunaan `http.HandleFunc()`
Fungsi ini digunakan untuk **routing**, menentukan aksi dari sebuah url tertentu ketika diakses (di sini url tersebut kita sebut sebagai rute/route). Rute dituliskan dalam `string` sebagai parameter pertama, dan aksi-nya sendiri dibungkus dalam fungsi (bisa berupa closure) yang ditempatkan pada parameter kedua (kita sebut sebagai handler).
@@ -83,7 +83,7 @@ Ketika rute-rute tersebut diakses lewat browser, outpunya adalah isi-handler dar
> Pada contoh di atas, ketika rute yang tidak terdaftar diakses, secara otomatis handler rute `/` akan terpanggil.
-#### • Penjelasan Mengenai **Handler**
+#### ◉ Penjelasan Mengenai **Handler**
Route handler atau handler atau parameter kedua fungsi `http.HandleFunc()`, adalah sebuah fungsi dengan ber-skema `func (ResponseWriter, *Request)`.
@@ -105,7 +105,7 @@ Pada contoh program yang telah kita buat, handler `Index()` memunculkan text `"W
Sebuah handler bisa dipergunakan pada banyak rute, bisa dilihat pada di atas handler `Index()` digunakan pada rute `/` dan `/index`.
-#### • Penggunaan `http.ListenAndServe()`
+#### ◉ Penggunaan `http.ListenAndServe()`
Fungsi ini digunakan untuk membuat web server baru. Pada contoh yang telah dibuat, web server di-*start* pada port `9000` (bisa dituliskan dalam bentuk `localhost:9000` atau cukup `:9000` saja).
diff --git a/content/B-http-basic-auth.md b/content/B-http-basic-auth.md
index 91d0f47ab..5350c63e3 100644
--- a/content/B-http-basic-auth.md
+++ b/content/B-http-basic-auth.md
@@ -149,7 +149,7 @@ func init() {
Selanjutnya, kita perlu menyiapkan beberapa fungsi yg digunakan pada `main.go`, yaitu `Auth()` dan `AllowOnlyGET()`.
-#### • Fungsi `Auth()`
+#### ◉ Fungsi `Auth()`
Buka `middleware.go`, siapkan fungsi `Auth()`.
@@ -188,7 +188,7 @@ Fungsi `r.BasicAuth()` mengembalikan 3 informasi:
Jika basic auth request tidak valid, maka tampilkan pesan error sebagai nilai balik. Sedangkan jika basic auth adalah valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang ada di aplikasi kita.
-#### • Fungsi `AllowOnlyGET()`
+#### ◉ Fungsi `AllowOnlyGET()`
Fungsi ini bertugas untuk memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka akan dianggap invalid request.
diff --git a/content/B-simple-configuration.md b/content/B-simple-configuration.md
index 8516fea7e..223e19d31 100644
--- a/content/B-simple-configuration.md
+++ b/content/B-simple-configuration.md
@@ -202,11 +202,11 @@ Coba ubah konfigurasi pada `config.json` nilai `log.verbose` menjadi `false`. La
Ok, kita telah selesai belajar tentang cara membuat file konfigurasi yang mudah dibaca dan praktis. Namun penerapan kontrol konfigurasi dengan metode ini kurang dianjurkan karena beberapa hal:
-#### • Tidak mendukung komentar
+#### ◉ Tidak mendukung komentar
Komentar sangat penting karena untuk aplikasi besar yang konfigurasi item-nya sangat banyak - akan susah untuk dipahami. Sebenarnya perihal ini bisa di-*resolve* menggunakan jenis konfigurasi lain seperti `YAML`, `.env`, atau lainnya.
-#### • Nilai konfigurasi harus diketahui di awal
+#### ◉ Nilai konfigurasi harus diketahui di awal
Kita harus tau semua value tiap-tiap konfigurasi terlebih dahulu, dan dituliskan ke file, sebelum aplikasi di-up. Dari sini akan sangat susah jika misal ada beberapa konfigurasi yang kita tidak tau nilainya tapi tau cara pengambilannya.
@@ -214,19 +214,19 @@ Contohnya pada beberapa kasus, seperti di AWS, database server yang di-setup sec
Dengan ini akan sangat susah jika kita harus cari terlebih dahulu value konfigurasi tersebut untuk kemudian dituliskan ke file. Memakan waktu dan kurang baik dari banyak sisi.
-#### • Tidak terpusat
+#### ◉ Tidak terpusat
Dalam pengembangan aplikasi, banyak konfigurasi yang nilai-nya akan didapat lewat jalan lain, seperti *environment variables* atau *command arguments*.
Akan lebih mudah jika hanya ada satu sumber konfigurasi saja untuk dijadikan acuan.
-#### • Statis (tidak dinamis)
+#### ◉ Statis (tidak dinamis)
Konfigurasi umumnya dibaca hanya jika diperlukan. Penulisan konfigurasi dalam file membuat proses pembacaan file harus dilakukan di awal, haru kemudian kita bisa ambil nilai konfigurasi dari data yang sudah ada di memori.
Hal tersebut memiliki beberapa konsekuensi, untuk aplikasi yang di-manage secara automated, sangat mungkin adanya perubahan nilai konfigurasi. Dari sini berarti pembacaan konfigurasi file tidak boleh hanya dilakukan di awal saja. Tapi juga tidak boleh dilakukan di setiap waktu, karena membaca file itu ada *cost*-nya dari prespektif I/O.
-#### • Solusi
+#### ◉ Solusi
Kita akan membahas solusi dari beberapa masalah di atas pada chapter terpisah, yaitu [Best Practice Configuration Menggunakan Environment Variable
](/C-best-practice-configuration-env-var)
diff --git a/content/B-template-functions.md b/content/B-template-functions.md
index b80d8e9ec..86b59b70d 100644
--- a/content/B-template-functions.md
+++ b/content/B-template-functions.md
@@ -168,7 +168,7 @@ Cara penggunannya juga masih sama.
```
-Output:
+Output program:
![Fungsi String](images/B_template_functions_4_string_func.png)
@@ -205,7 +205,7 @@ Berikut merupakan contoh penerapan fungsi `len` dan `index`.
```
-Output:
+Output program:
![Fungsi `len` dan `index`](images/B_template_functions_5_len_index.png)
@@ -224,7 +224,7 @@ Selain fungsi operator perbandingan, terdapat juga operator logika `or`, `and`,
{{end}}
```
-Output:
+Output program:
![Fungsi `or`, `and`, dan `not`](images/B_template_functions_6_or_and_not.png)
diff --git a/content/C-best-practice-configuration-env-var.md b/content/C-best-practice-configuration-env-var.md
index b0738d442..97aa1dd91 100644
--- a/content/C-best-practice-configuration-env-var.md
+++ b/content/C-best-practice-configuration-env-var.md
@@ -6,7 +6,7 @@ Pada bagian ini kita akan mempelajari penerapan konfigurasi pada *environment va
*Environment variable* merupakan variabel yang berada di lapisan *runtime* sistem operasi. Karena *env var* atau *environment variable* merupakan variabel seperti pada umumnya, maka kita bisa melakukan operasi seperti mengubah nilainya atau mengambil nilainya.
-Salah satu *env var* yang mungkin sering temen-temen temui adalah `PATH`. `PATH` sendiri merupakan variabel yang digunakan oleh sistem operasi untuk men-*specify* direktori tempat di mana *binary* atau *executable* berada.
+Salah satu *env var* yang mungkin sering pembaca temui adalah `PATH`. `PATH` sendiri merupakan variabel yang digunakan oleh sistem operasi untuk men-*specify* direktori tempat di mana *binary* atau *executable* berada.
Default-nya, sistem operasi pasti mempunyai beberapa *env var* yang sudah ada tanpa kita set, salah satunya seperti `PATH` tadi, juga lainnya. Variabel-variabel tersebut digunakan oleh sistem operasi untuk keperluan mereka. Tapi karena variabel juga bisa diakses oleh kita (selaku developer), maka kita pun juga bisa mempergunakannya untuk kebutuhan tertentu.
@@ -138,7 +138,7 @@ set SERVER_WRITE_TIMEOUT_IN_MINUTE=2
go run main.go
```
-Agak sedikit report memang untuk bagian ini, tapi mungkin bisa diperingkas dengan membuat file `.sh` untuk non-Windows, dan file `.bat` untuk Windows. Jadi nanti bisa tinggal eksekusi sh/bat-nya saja. Atau temen-temen bisa tulis saja dalam `Makefile`. Untuk windows bisa kok eksekusi command `make` caranya dengan install [make](https://chocolatey.org/packages/make) lewat [Chocolatey](https://chocolatey.org/).
+Agak sedikit report memang untuk bagian ini, tapi mungkin bisa diperingkas dengan membuat file `.sh` untuk non-Windows, dan file `.bat` untuk Windows. Jadi nanti bisa tinggal eksekusi sh/bat-nya saja. Atau pembaca bisa tulis saja dalam `Makefile`. Untuk windows bisa kok eksekusi command `make` caranya dengan install [make](https://chocolatey.org/packages/make) lewat [Chocolatey](https://chocolatey.org/).
Berikut adalah penampakan contoh run program lewat bat-file di Windows.
@@ -148,7 +148,7 @@ Berikut adalah penampakan contoh run program lewat bat-file di Windows.
Memang saya setuju jika lebih butuh *effort* baik dari sisi programming maupun dari sisi eksekusi program-nya. Tapi *trust me*, pada production yang notabene *deployment* di-automatisasi (entah itu container based, pakai orchestrator, maupun tidak), pasti lebih mudah.
-Mungkin dari sini temen-temen bisa lanjut ke chapter [C.35. Dockerize Aplikasi Golang](/C-dockerize-golang.html) untuk melihat praktek nyata penerapan konfigurasi via *env var*.
+Mungkin dari sini pembaca bisa lanjut ke chapter [C.35. Dockerize Aplikasi Golang](/C-dockerize-golang.html) untuk melihat praktek nyata penerapan konfigurasi via *env var*.
---
diff --git a/content/C-cors-preflight-request.md b/content/C-cors-preflight-request.md
index 4fefa8da0..b1680b359 100644
--- a/content/C-cors-preflight-request.md
+++ b/content/C-cors-preflight-request.md
@@ -91,7 +91,7 @@ Khusus untuk beberapa header seperti `Accept`, `Origin`, `Referer`, dan `User-Ag
## C.14.3. Testing CORS
-#### • Persiapan
+#### ◉ Persiapan
Ada beberapa hal yang perlu dipersiapkan. Pertama, pastikan punya google chrome. Lalu install extension [jQuery Injector](https://chrome.google.com/webstore/detail/jquery-injector/ekkjohcjbjcjjifokpingdbdlfekjcgi?hl=en). Buka https://www.google.com lalu inject jQuery. Dengan melakukan inject jQuery secara paksa maka dari situs google kita bisa menggunakan jQuery.
@@ -109,7 +109,7 @@ Silakan lihat gambar berikut untuk memperjelas.
Bisa dilihat, tidak ada error, karena memang request dari google diijinkan. Silakan coba-coba melakukan request AJAX lainnya dengan method POST, DELETE, atau lainnya; atau ditambah dengan menyisipkan header tertentu dalam ajax request.
-#### • Akses http://localhost:9000 dari Origin yang Tidak Didaftarkan di CORS
+#### ◉ Akses http://localhost:9000 dari Origin yang Tidak Didaftarkan di CORS
Selanjutnya coba buka tab baru, buka https://novalagung.com, lalu jalankan script yang sama.
@@ -119,7 +119,7 @@ Selanjutnya coba buka tab baru, buka https://novalagung.com, lalu jalankan scrip
Dari screenshot dan error log di atas, bisa dilihat bahwa request gagal. Hal ini dikarenakan origin https://novalagung.com tidak diijinkan untuk mengakses http://localhost:9000.
-#### • CORS Multiple Origin
+#### ◉ CORS Multiple Origin
Sekarang coba tambahkan situs https://novalagung.com ke CORS header.
@@ -141,7 +141,7 @@ Masih tetap error, tapi berbeda dengan error sebelumnya.
Sebenarnya sudah kita singgung juga di atas, bahwa di spesifikasi adalah diperbolehkan isi header `Access-Control-Allow-Origin` lebih dari satu website. Namun, kebanyakan browser tidak mendukung bagian ini. Oleh karena itu error di atas muncul. Konfigurasi ini termasuk tidak valid, hasilnya kedua website tersebut tidak punya ijin masuk.
-#### • Allow All
+#### ◉ Allow All
Gunakan tanda asteriks (`*`) sebagai nilai ketiga CORS header untuk memberi ijin ke semua.
@@ -158,7 +158,7 @@ w.Header().Set("Access-Control-Allow-Headers", "*")
## C.14.4. Preflight Request
-#### • Teori
+#### ◉ Teori
Dalam konteks CORS, request dikategorikan menjadi 2 yaitu, **Simple Request** dan **Preflighted Request**. Beberapa contoh request yang sudah kita pelajari di atas termasuk simple request.
@@ -206,7 +206,7 @@ http.HandleFunc("/index", func(w http.ResponseWriter, r *http.Request) {
})
```
-#### • Praktek
+#### ◉ Praktek
Langsung saja buka google.com lalu lakukan AJAX request yang memenuhi alah satu kriteria preflighted request, misalnya, gunakan header `Content-Type: application/json`.
diff --git a/content/C-dockerize-golang.md b/content/C-dockerize-golang.md
index 010077fdd..b6b41990e 100644
--- a/content/C-dockerize-golang.md
+++ b/content/C-dockerize-golang.md
@@ -2,7 +2,7 @@
Pada chapter ini kita akan praktek men-*dockerize* aplikasi Go, membungkus aplikasi hello world sebagai docker image untuk kemudian di jalankan sebagai container.
-Kita tidak akan membahas docker secara detail ya, hanya pengenalannya saja. Untuk teman-teman yang tertarik belajar docker secara komprehensif mulai dari awal, hingga masuk ke docker compose kemudian kubernetes *from zero to hero*, bisa *enroll* course Udemy [Praktis Belajar Docker dan Kubernetes untuk Pemula](https://www.udemy.com/course/praktis-belajar-docker-dan-kubernetes-untuk-pemula/) berikut.
+Kita tidak akan membahas docker secara detail ya, hanya pengenalannya saja. Untuk pembaca yang tertarik belajar docker secara komprehensif mulai dari awal, hingga masuk ke docker compose kemudian kubernetes *from zero to hero*, bisa *enroll* course Udemy [Praktis Belajar Docker dan Kubernetes untuk Pemula](https://www.udemy.com/course/praktis-belajar-docker-dan-kubernetes-untuk-pemula/) berikut.
@@ -14,23 +14,23 @@ Pastikan Docker Engine ter-*install* untuk pengguna Windows atau MacOS. Untuk pe
## C.35.2. Istilah Dalam Docker
-#### • Container
+#### ◉ Container
Container adalah sebuah environment ter-isolasi, merupakan bentuk virtualisasi yang lebih kecil dan ringan dibanding VM (Virtual Machine). Virtualisasi pada container disebut dengan *Containerization*.
-#### • Docker Container
+#### ◉ Docker Container
Docker container adalah sebuah container yang di-manage oleh Docker Engine.
-#### • Docker Engine
+#### ◉ Docker Engine
Docker engine merupakan *daemon* yang bertugas untuk manajemen container-container.
-#### • Docker Image
+#### ◉ Docker Image
Docker Image adalah sebuah file yang di-*generate* oleh docker, yang file tersebut nantinya digunakan untuk basis pembuatan dan eksekusi container.
-#### • Containerize dan Dockerize
+#### ◉ Containerize dan Dockerize
Containerize merupakan istilah terhadap aplikasi yang di-*build* ke bentuk Image. Sedangkan Dockerize merupakan istilah untuk containerize menggunakan Docker. Perlu diketahui bahwa penyedia container tidak hanya Docker saja, ada banyak engine container lainnya yang bisa dipergunakan.
@@ -189,7 +189,7 @@ Ok, file `Dockerfile` sudah siap, mari kita lanjut ke proses *build* dan *start
## C.35.6. *Build Image* dan *Create Container*
-#### • Build Image
+#### ◉ Build Image
Pertama masuk ke direktori folder project, lalu jalankan *command* `docker build` berikut.
@@ -204,7 +204,7 @@ Kurang lebih outputnya seperti gambar berikut. O iya gunakan *command* `docker i
![Build Image](images/C_dockerize_golang_2_build_image.png)
-#### • Create Container
+#### ◉ Create Container
Image sudah siap, sekarang mari kita buat container baru menggunakan basis image `my-image-hello-world`. *Command*-nya kurang lebih berikut:
@@ -227,7 +227,7 @@ Semoga cukup jelas penjabaran di atas. Setelah container berhasil dibuat, cek me
![Create Container](images/C_dockerize_golang_3_create_container.png)
-#### • Start Container
+#### ◉ Start Container
Ok, sekarang container juga sudah dibuat, lanjut untuk *start* container tersebut, caranya menggunakan command `docker container start`. Jika sudah, coba cek di browser aplikasi web hello world, harusnya sudah bisa diakses.
@@ -244,7 +244,7 @@ Jika mengalami error saat start container, bisa jadi karena port `8080` sudak di
O iya, pada image di atas juga bisa dilihat penggunaan *command* `docker container ls` untuk memunculkan list container yang sedand *running* atau aktif. Untuk menampilkan semua container (aktif maupun non-aktif), cukup dengan menambahkan flag `-a` atau `--all`.
-#### • Stop Container
+#### ◉ Stop Container
Untuk stop container bisa dengan *command* `docker container stop `.
@@ -253,7 +253,7 @@ docker container stop my-container-hello-world
docker container ls
```
-#### • Hapus Container
+#### ◉ Hapus Container
Untuk hapus container bisa dengan *command* `docker container rm `.
@@ -262,7 +262,7 @@ docker container rm my-container-hello-world
docker container ls
```
-#### • Hapus Image
+#### ◉ Hapus Image
Untuk hapus image bisa dengan *command* `docker image rm `. O iya, untuk penghapusan image, harus dipastikan terlebih dahulu tidak ada container yang running menggunakan basis image yang ingin dihapus.
@@ -279,14 +279,14 @@ Atau bisa juga menggunakan *command* `docker run`. *Command* ini akan membuat co
Mungkin perbandingannya seperti ini:
-#### • Jalankan container lewat `create` lalu `start`
+#### ◉ Jalankan container lewat `create` lalu `start`
```bash
docker container create --name my-container-hello-world -e PORT=8080 -e INSTANCE_ID="my first instance" -p 8080:8080 my-image-hello-world
docker container start my-container-hello-world
```
-#### • Jalankan container lewat `run`
+#### ◉ Jalankan container lewat `run`
```bash
docker container run --name my-container-hello-world -e PORT=8080 -e INSTANCE_ID="my first instance" -p 8080:8080 my-image-hello-world
@@ -300,11 +300,11 @@ O iya, khusus untuk *command* `docker run` biasanya dijalankan dengan tambahan b
docker container run --name my-container-hello-world --rm -it -e PORT=8080 -e INSTANCE_ID="my first instance" -p 8080:8080 my-image-hello-world
```
-#### • Flag `--rm`
+#### ◉ Flag `--rm`
-Flag ini digunakan untuk meng-automatisasi proses penghapusan container sewaktu container tersebut di stop. Jadi kita tidak perlu delete manual pakai `docker container rm`. Hal ini sangat membantu karena *command* `docker run` akan membuat container baru setiap dijalankan. Tapi sebenarnya pada contoh sebelumnya kita tidak perlu khawatir akan dibuat container baru karena sudah ada flag `--name`. Flag tersebut digunakan untuk menentukan nama container, yang di mana nama container harus unik. Jadi kalau ada duplikasi pasti langsung error. Nah dari sini berarti kalau temen-temen tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`.
+Flag ini digunakan untuk meng-automatisasi proses penghapusan container sewaktu container tersebut di stop. Jadi kita tidak perlu delete manual pakai `docker container rm`. Hal ini sangat membantu karena *command* `docker run` akan membuat container baru setiap dijalankan. Tapi sebenarnya pada contoh sebelumnya kita tidak perlu khawatir akan dibuat container baru karena sudah ada flag `--name`. Flag tersebut digunakan untuk menentukan nama container, yang di mana nama container harus unik. Jadi kalau ada duplikasi pasti langsung error. Nah dari sini berarti kalau pembaca tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`.
-#### • Flag `-it`
+#### ◉ Flag `-it`
Flag ini merupakan flag gabungan antara `-i` yang digunakan untuk meng-enable *interactive mode* dan `-t` untuk *enable* `TTY`. Dengan ini kita bisa masuk ke mode interaktif yang di mana jika kita terminate atau kill command menggunakan `CTRL + C` atau `CMD + C` (untuk mac), maka otomatis container akan di stop.
diff --git a/content/C-echo-routing.md b/content/C-echo-routing.md
index b9a11d5d3..9ea7e7a1d 100644
--- a/content/C-echo-routing.md
+++ b/content/C-echo-routing.md
@@ -79,7 +79,7 @@ Method `ctx.String()` dari objek context milik handler digunakan untuk mempermud
Selain `ctx.String()` ada banyak method sejenis lainnya, berikut selengkapnya.
-#### • Method `.String()`
+#### ◉ Method `.String()`
Digunakan untuk render plain text sebagai output (isi response header `Content-Type` adalah `text/plain`). Method ini tugasnya sama dengan method `.Write()` milik objek `http.ResponseWriter`.
@@ -90,7 +90,7 @@ r.GET("/index", func(ctx echo.Context) error {
})
```
-#### • Method `.HTML()`
+#### ◉ Method `.HTML()`
Digunakan untuk render html sebagai output. Isi response header `Content-Type` adalah `text/html`.
@@ -101,7 +101,7 @@ r.GET("/html", func(ctx echo.Context) error {
})
```
-#### • Method `.Redirect()`
+#### ◉ Method `.Redirect()`
Digunakan untuk redirect, pengganti `http.Redirect()`.
@@ -111,7 +111,7 @@ r.GET("/index", func(ctx echo.Context) error {
})
```
-#### • Method `.JSON()`
+#### ◉ Method `.JSON()`
Digunakan untuk render data JSON sebagai output. Isi response header `Content-Type` adalah `application/json`.
@@ -126,7 +126,7 @@ r.GET("/json", func(ctx echo.Context) error {
Echo juga menyediakan beberapa method untuk keperluan parsing request, di antaranya:
-#### • Parsing Query String
+#### ◉ Parsing Query String
Method `.QueryParam()` digunakan untuk mengambil data pada query string request, sesuai dengan key yang diinginkan.
@@ -145,7 +145,7 @@ Test menggunakan curl:
curl -X GET http://localhost:9000/page1?name=grayson
```
-#### • Parsing URL Path Param
+#### ◉ Parsing URL Path Param
Method `.Param()` digunakan untuk mengambil data path parameter sesuai skema rute.
@@ -166,7 +166,7 @@ Test menggunakan curl:
curl -X GET http://localhost:9000/page2/grayson
```
-#### • Parsing URL Path Param dan Setelahnya
+#### ◉ Parsing URL Path Param dan Setelahnya
Selain mengambil parameter sesuai spesifik path, kita juga bisa mengambil data **parameter path dan setelahnya**.
@@ -189,7 +189,7 @@ Test menggunakan curl:
curl -X GET http://localhost:9000/page3/tim/need/some/sleep
```
-#### • Parsing Form Data
+#### ◉ Parsing Form Data
Data yang dikirim sebagai request body dengan jenis adalah Form Data bisa di-ambil dengan mudah menggunakan `ctx.FormValue()`.
diff --git a/content/C-golang-aws-s3.md b/content/C-golang-aws-s3.md
index dc2e02934..b731b568c 100644
--- a/content/C-golang-aws-s3.md
+++ b/content/C-golang-aws-s3.md
@@ -14,11 +14,11 @@ Pada dasarnya Simple Storage Service (S3) adalah layanan penyimpanan file/object
Beberapa istilah yang biasa kita temukan saat kita bekerja dengan Amazon S3 antara lain:
-#### • Bucket
+#### ◉ Bucket
Bucket adalah wadah untuk object bisa disimpan ke dalam Amazon S3. Kita bisa menganalogikan bucket seperti directory yang ada di harddisk kita, dimana kita bisa membuat folder/path dan menyimpan file di dalamnya. Seperti contoh, misal kita membuat bucket `adamstudio-bucket` di region `ap-southeast-1` dan mengupload file `adamstudio.jpg`, maka kita bisa mengakses file tersebut dengan URL `https://adamstudio-bucket.s3.ap-southeast-1.amazonaws.com/adamstudio.jpg` (dengan authorisasi tertentu pastinya).
-#### • Object
+#### ◉ Object
Object secara singkat bisa kita artikan sebagai file, meskipun pada dasarnya berbeda, karena object juga menyimpan metadata file dan data-data lainnya.
diff --git a/content/C-golang-grpc-protobuf.md b/content/C-golang-grpc-protobuf.md
index 744fbff20..f202307db 100644
--- a/content/C-golang-grpc-protobuf.md
+++ b/content/C-golang-grpc-protobuf.md
@@ -59,18 +59,18 @@ Salah satu pembeda yang paling terlihat dibanding chapter sebelumnya adalah di s
Lanjut. Di bawah ini merupakan penjelasan per bagian dari struktur project di atas.
-#### • Folder `common`
+#### ◉ Folder `common`
Folder `common`, berisikan 2 buah sub folder, `config` dan `model`.
- Folder `config` berisikan informasi shared atau global, yang digunakan aplikasi client maupun server.
- Folder `model` berisikan file `.proto`. Silakan salin file `garage.proto` dan `user.proto` pada chapter sebelumnya ke folder tersebut.
-#### • Folder `client`
+#### ◉ Folder `client`
Isinya adalah satu buah file main, yang nantinya di jalankan sebagai aplikasi client. Aplikasi client ini akan berkomunikasi dengan 2 buah aplikasi server.
-#### • Folder `services`
+#### ◉ Folder `services`
Satu buah file proto untuk satu aplikasi rpc server (service). Karena ada dua file proto, berarti jelasnya ada dua aplikasi rpc server, `service-user` dan `service-garage`. Folder `services` ini menampung kedua aplikasi service tersebut.
@@ -97,7 +97,7 @@ Keyword `service` digunakan untuk membuat service. Service ini nantinya juga iku
OK, sekarang tambahkan kode berikut ke file proto.
-#### • Service `Users`
+#### ◉ Service `Users`
Buka file `user.proto`, tambahkan kode berikut di akhir baris.
@@ -147,7 +147,7 @@ Setelah di-compile, dua buah interface terbuat dengan skema nama ``.
@@ -391,7 +391,7 @@ curl -X GET \
http://localhost:8080/index
```
-Output:
+Output program:
![JWT Authorization](images/C_golang_jwt_3_jwt_authorization.png)
diff --git a/content/C-golang-ldap-authentication.md b/content/C-golang-ldap-authentication.md
index 5c058e9af..17fdb17a3 100644
--- a/content/C-golang-ldap-authentication.md
+++ b/content/C-golang-ldap-authentication.md
@@ -4,11 +4,11 @@ Pada chapter ini kita belajar mengenai otentikasi user ke *Directory Service* le
## C.33.1. Definisi
-#### • LDAP
+#### ◉ LDAP
LDAP (Lightweight Directory Access Protocol) adalah protokol yang digunakan untuk mengakses **Directory Services** dalam sebuah komunikasi client-server.
-#### • Directory Services
+#### ◉ Directory Services
Directory Services adalah sebuah sistem yang menyimpan, mengelola, dan menyediakan akses informasi untuk menghubungkan sumber daya network (atau network resources). Network resources yang dimaksud contohnya:
@@ -26,11 +26,11 @@ Dengan terhubungnya resources tersebut, akan mudah bagi kita untuk mengelola ban
Selain itu, juga LDAP sering dimanfaatkan dalam implementasi SSO (Single sign-on).
-#### • Bind Operation
+#### ◉ Bind Operation
Operasi bind digunakan untuk otentikasi client ke directory server, dan juga untuk mengubah state otorisasi client tersebut. Operasi bind dilakukan dengan mengirim informasi bind dn dan password.
-#### • Directory Server untuk Testing
+#### ◉ Directory Server untuk Testing
Karena komunikasi adalah client-server maka kita perlu menggunakan salah satu directory server untuk keperluan testing. Beruntung-nya [Forum Systems](http://www.forumsys.com) berbaik hati menyediakan directory server yg bisa diakses secara gratis oleh public, dan pada chapter ini akan kita menggunakannya.
diff --git a/content/C-golang-protobuf-implementation.md b/content/C-golang-protobuf-implementation.md
index 72621f4cc..9f363cd4f 100644
--- a/content/C-golang-protobuf-implementation.md
+++ b/content/C-golang-protobuf-implementation.md
@@ -62,7 +62,7 @@ tree .
Folder `yourproject/model` berisikan file-file `.proto` (dua buah file proto didefinisikan). Dari kedua file di atas akan di-generate file model `.go` menggunakan command `protoc`. Nantinya generated file tersebut dipakai dalam `main.go`.
-#### • File `user.proto`
+#### ◉ File `user.proto`
OK, mari kita masuk ke bagian tulis-menulis kode. Buka file `user.proto`, tulis kode berikut.
@@ -152,7 +152,7 @@ type UserList struct {
}
```
-#### • File `garage.proto`
+#### ◉ File `garage.proto`
Sekarang beralih ke file ke-dua, `garage.proto`. Silakan tulis kode berikut.
@@ -313,7 +313,7 @@ Package model yang isinya generated proto file, di-import. Dari package tersebut
}
```
-#### • Print proto object
+#### ◉ Print proto object
Print salah satu objek yang sudah dibuat di atas.
@@ -333,7 +333,7 @@ Pada statement print pertama, objek ditampilkan apa adanya. Generated struct mem
Di statement print kedua, method `.String()` diakses, menampilkan semua property yang didefinisikan dalam proto message (property `XXX_` tidak dimunculkan).
-#### • Konversi objek proto ke json string
+#### ◉ Konversi objek proto ke json string
Tambahkan kode berikut:
@@ -361,7 +361,7 @@ Jalankan aplikasi, cek hasilnya.
Selain method `.Marshal()`, konversi ke json string bisa dilakukan lewat method `.MarshalToString()`.
-#### • Konversi json string ke objek proto
+#### ◉ Konversi json string ke objek proto
Proses unmarshal dari json string ke objek proto, dapat menggunakan `protojson.Unmarshal`, dengan parameter pertama disisipi data json byte dan parameter kedua disisipi objek proto pointer.
diff --git a/content/C-golang-sso-saml-sp.md b/content/C-golang-sso-saml-sp.md
index d0504ae66..45db97f02 100644
--- a/content/C-golang-sso-saml-sp.md
+++ b/content/C-golang-sso-saml-sp.md
@@ -8,7 +8,7 @@ Kali ini topik yang dipilih adalah SAML SSO versi 2.0. Kita akan pelajari cara p
Sebelum kita masuk ke bagian tulis menulis kode, alangkah baiknya sedikit membahas tentang definisi dari SSO dan SAML itu sendiri.
-#### • SSO
+#### ◉ SSO
SSO atau Single Sign-On merupakan servis untuk otentikasi dan manajemen session. Dengan SSO, maka akses ke banyak aplikasi cukup bisa sekali otentikasi saja. Contoh SSO:
@@ -20,7 +20,7 @@ SSO atau Single Sign-On merupakan servis untuk otentikasi dan manajemen session.
Ada beberapa jenis penerapan SSO yang bisa dipilih, salah satunya adalah **Security Assertion Markup Language** atau **SAML** yang akan kita bahas pada chapter ini.
-#### • SAML
+#### ◉ SAML
SAML merupakan protokol open standard untuk otentikasi dan otorisasi antara penyedia layanan (**Service Provider**) dan penyedia identitas (**Identity Provider**). SAML berbasis *assertion* berupa XML.
@@ -112,7 +112,7 @@ Karena user memiliki security context, maka SP merespon dengan balasan berupa ta
> Selanjutnya, setiap kali ada request target resource, maka point 7 dan 8 akan diulang.
-![SAML Flow](https://upload.wikimedia.org/wikipedia/en/thumb/0/04/Saml2-browser-sso-redirect-post.png/600px-Saml2-browser-sso-redirect-post.png)
+![SAML Flow](https://images.ctfassets.net/23aumh6u8s0i/2iNQwccfZWMELfLjk65SVe/b9b4427c66165a56c8800c3e391e8fd0/saml-flow-diagram)
## C.34.4. SAML Metadata
@@ -126,7 +126,7 @@ Metadata diperlukan secara *mandatory* dalam operasi dan komunikasi antar SP dan
> Metadata berisi informasi penting. Di antaranya adalah entity ID, key pair, protocol endpoints, dan lainnya.
-![Metadata share](https://upload.wikimedia.org/wikipedia/en/thumb/c/c3/SAML_Web_Browser_SSO_With_Static_Metadata.png/440px-SAML_Web_Browser_SSO_With_Static_Metadata.png)
+![Metadata share](https://images.ctfassets.net/23aumh6u8s0i/60UOe2pqoOcYFJqeGMzfA7/e0dd3cdd9a12b682fa648f27869b9588/SAMLResponse)
Ada dua jenis metode pertukaran metadata, **static metadata exchange** dan **dynamic metadata exchange**. Yang kita terapkan pada pembahasan ini adalah yg static.
diff --git a/content/C-https-tls.md b/content/C-https-tls.md
index 686874b94..70e63931f 100644
--- a/content/C-https-tls.md
+++ b/content/C-https-tls.md
@@ -4,11 +4,11 @@ Pada bagian ini kita akan belajar cara meng-enable fasilitas SSL/TLS pada web se
## C.24.1. Definisi
-#### • SSL
+#### ◉ SSL
**SSL, Secure Sockets Layer**, adalah standar untuk pengamanan komunikasi lewat internet. Data atau informasi yang sedang dikomunikasikan dari sebuah system ke system lain akan di-proteksi, dengan cara adalah mengacak informasi tersebut menggunakan algoritma enkripsi.
-#### • SSL Certificates
+#### ◉ SSL Certificates
**SSL Certificate**, adalah sebuah file berisikan informasi mengenai website, yang nantinya dibutuhkan untuk enkripsi data. SSL Certificate berisi **Public Key**. Public key digunakan untuk meng-enkripsi data yang akan di transfer.
@@ -26,11 +26,11 @@ Berikut merupakan penjelasan dalam bentuk gambar yang diambil dari [coinjolt.com
Kedua file certificate dan file private key harus disimpan dengan sangat super aman di server.
-#### • TLS
+#### ◉ TLS
**TLS, Transport Layer Security**, adalah versi yang lebih update dari SSL.
-#### • HTTPS
+#### ◉ HTTPS
**HTTPS, Hyper Text Transfer Protocol Secure**, adalah ekstensi dari HTTP yang berguna untuk pengamanan komunikasi lewat internet. Data atau informasi yang dikomunikasikan di-enkripsi menggunakan **TLS**.
diff --git a/content/C-parsing-http-request-payload-echo.md b/content/C-parsing-http-request-payload-echo.md
index 3a4288166..c55917c5f 100644
--- a/content/C-parsing-http-request-payload-echo.md
+++ b/content/C-parsing-http-request-payload-echo.md
@@ -66,7 +66,7 @@ Jalankan aplikasi, lakukan testing. Bisa gunakan `curl` ataupun API testing tool
Di bawah ini shortcut untuk melakukan request menggunakan `curl` pada 4 jenis payload yang kita telah bahas. Response dari seluruh request adalah sama, menandakan bahwa data yang dikirim berhasil ditampung.
-#### • Form Data
+#### ◉ Form Data
```bash
curl -X POST http://localhost:9000/user \
@@ -76,7 +76,7 @@ curl -X POST http://localhost:9000/user \
# output => {"name":"Nope","email":"nope@novalagung.com"}
```
-#### • JSON Payload
+#### ◉ JSON Payload
```bash
curl -X POST http://localhost:9000/user \
@@ -86,7 +86,7 @@ curl -X POST http://localhost:9000/user \
# output => {"name":"Nope","email":"nope@novalagung.com"}
```
-#### • XML Payload
+#### ◉ XML Payload
```bash
curl -X POST http://localhost:9000/user \
@@ -100,7 +100,7 @@ curl -X POST http://localhost:9000/user \
# output => {"name":"Nope","email":"nope@novalagung.com"}
```
-#### • Query String
+#### ◉ Query String
```bash
curl -X GET http://localhost:9000/user?name=Joe&email=nope@novalagung.com
diff --git a/content/C-project-layout-structure.md b/content/C-project-layout-structure.md
index d7307d81c..d25798da5 100644
--- a/content/C-project-layout-structure.md
+++ b/content/C-project-layout-structure.md
@@ -109,7 +109,7 @@ Ada cukup banyak folder dan subfolder dalam project layout, berikut kami ringkas
└── ...
```
-Hmm, cukup banyak juga ya yang perlu dipelajari? 😅 Tenang, tidak perlu untuk dihafal, cukup dipahami saja. Selain itu semua direktori di atas juga belum tentu dipakai semua, perlu disesuaikan dengan proyek yang sedang teman-teman kembangkan.
+Hmm, cukup banyak juga ya yang perlu dipelajari? 😅 Tenang, tidak perlu untuk dihafal, cukup dipahami saja. Selain itu semua direktori di atas juga belum tentu dipakai semua, perlu disesuaikan dengan proyek yang sedang pembaca kembangkan.
Ok, sampai sini saja pembahasan mengenai project layout, selanjutnya silakan mencoba-coba jika berkenan, bisa dengan men-develop mulai awal, atau *clone* existing project untuk dipelajari strukturnya.
diff --git a/content/C-secure-middleware.md b/content/C-secure-middleware.md
index 895e66c01..df4469d40 100644
--- a/content/C-secure-middleware.md
+++ b/content/C-secure-middleware.md
@@ -65,7 +65,7 @@ func main() {
Pembuatan objek secure middleware dilakukan menggunakan `secure.New()` dengan isi parameter adalah konfigurasi. Bisa dilihat ada 5 buah property konfigurasi di-set. Berikut merupakan penjelasan tiap-tiap property tersebut.
-#### • Konfigurasi `AllowedHosts`
+#### ◉ Konfigurasi `AllowedHosts`
```go
AllowedHosts: []string{"localhost:9000", "www.google.com"}
@@ -73,7 +73,7 @@ AllowedHosts: []string{"localhost:9000", "www.google.com"}
Host yang diperbolehkan mengakses web server ditentukan hanya 2, yaitu localhost:9000 yang merupakan web server itu sendiri, dan google.com. Silakan coba mengakses aplikasi kita ini menggunakan AJAX lewat google.com dan domainnya lainnya untuk mengetes apakah fungsionalitas nya berjalan.
-#### • Konfigurasi `FrameDeny`
+#### ◉ Konfigurasi `FrameDeny`
```go
FrameDeny: true
@@ -87,7 +87,7 @@ Di library secure, untuk men-disable ijin akses aplikasi dari dalam iframe, bisa
Untuk mengetes, silakan buat aplikasi web terpisah yang mer-render sebuah view. Dalam view tersebut siapkan satu buah iframe yang mengarah ke `https://localhost:9000/index`.
-#### • Konfigurasi `CustomFrameOptionsValue`
+#### ◉ Konfigurasi `CustomFrameOptionsValue`
```go
CustomFrameOptionsValue: "SAMEORIGIN"
@@ -99,7 +99,7 @@ Dengan menambahkan satu buah property lagi yaitu `CustomFrameOptionsValue: "SAME
Untuk mengetes, buat rute baru yang me-render sebuah view. Dalam view tersebut siapkan satu buah iframe yang mengarah ke `/index`.
-#### • Konfigurasi `ContentTypeNosniff`
+#### ◉ Konfigurasi `ContentTypeNosniff`
```go
ContentTypeNosniff: true
@@ -107,7 +107,7 @@ ContentTypeNosniff: true
Property `ContentTypeNosniff: true` digunakan untuk disable MIME-sniffing yang dilakukan oleh browser IE. Lebih jelasnya silakan baca [X-Content-Type-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options).
-#### • Konfigurasi `BrowserXssFilter`
+#### ◉ Konfigurasi `BrowserXssFilter`
```go
BrowserXssFilter: true
diff --git a/content/C-write-pdf-file.md b/content/C-write-pdf-file.md
index 834331be1..ce7d0529f 100644
--- a/content/C-write-pdf-file.md
+++ b/content/C-write-pdf-file.md
@@ -45,23 +45,23 @@ Statement `gofpdf.New()` digunakan untuk membuat objek dokumen baru. Fungsi `.Ne
Fungsi `.New()` mengembalikan objek PDF. Dari situ kita bisa mengakses banyak method sesuai kebutuhan, beberapa di antaranya adalah 4 buah method yang dicontohkan di atas.
-#### • Method `.AddPage()`
+#### ◉ Method `.AddPage()`
Method ini digunakan untuk menambah halaman baru. Defaultnya, objek dokumen yang baru dibuat tidak memiliki halaman. Dengan memanggil `.AddPage()` maka halaman baru dibuat.
Setelah *at least* satu halaman tersedia, kita bisa lanjut ke proses tulis menulis.
-#### • Method `.SetFont()`
+#### ◉ Method `.SetFont()`
Method ini digunakan untuk menge-set konfigurasi font dokumen. Font Family, Font Style, dan Font Size disisipkan dalam parameter secara berurutan.
-#### • Method `.Text()`
+#### ◉ Method `.Text()`
Digunakan untuk menulis text pada koordinat tertentu. Pada kode di atas, `40` artinya `40mm` dari kiri, sedangkan `10` artinya `10mm` dari atas. Satuan milimeter digunakan karena pada saat penciptaan objek dipilih `mm` sebagai satuan.
Method ini melakukan penulisan text pada current page.
-#### • Method `.Image()`
+#### ◉ Method `.Image()`
Digunakan untuk menambahkan image. Method ini memerlukan beberapa parameter.
diff --git a/content/CONTRIBUTING.md b/content/CONTRIBUTING.md
index 881de87ea..87f6be0b0 100644
--- a/content/CONTRIBUTING.md
+++ b/content/CONTRIBUTING.md
@@ -1,6 +1,6 @@
# Author & Contributors
-E-book Dasar Pemrograman Golang adalah proyek *open source*. Siapapun bebas untuk berkontribusi di sini, bisa dalam bentuk perbaikan *typo*, update kalimat, maupun submit tulisan baru. Bagi teman-teman yang berminat untuk berkontribusi, silakan ikuti petunjuk berikut:
+E-book Dasar Pemrograman Golang adalah proyek *open source*. Siapapun bebas untuk berkontribusi di sini, bisa dalam bentuk perbaikan *typo*, update kalimat, maupun submit tulisan baru. Bagi pembaca yang berminat untuk berkontribusi, silakan ikuti petunjuk berikut:
1. *Fork* https://github.com/novalagung/dasarpemrogramangolang.
1. *Commit* perbaikan anda ke branch baru.
@@ -61,6 +61,7 @@ Berikut merupakan *hall of fame* kontributor yang sudah berbaik hati menyisihkan
1. [MH Rohman Masyhar](https://github.com/rohmanhm)
1. [Muhammad Faris 'Afif](https://github.com/muhfaris)
1. [Muhammad Ridho](https://github.com/reedho)
+1. [Muhammad Zulfan Wahyudin](https://github.com/mzulfanw)
1. [Mulia Nasution](https://github.com/mul14)
1. [nekonako](https://github.com/nekonako)
1. [Nuevo Querto](https://github.com/NuevoQuerto)
diff --git a/content/D-insert-1mil-csv-record-into-db-in-a-minute.md b/content/D-insert-1mil-csv-record-into-db-in-a-minute.md
index 02c65b138..b2c46a01c 100644
--- a/content/D-insert-1mil-csv-record-into-db-in-a-minute.md
+++ b/content/D-insert-1mil-csv-record-into-db-in-a-minute.md
@@ -6,13 +6,13 @@ Pada bagian insert data kita terapkan mekanisme failover, jadi ketika ada operas
## D.1.1. Penjelasan
-#### • Worker Pool
+#### ◉ Worker Pool
Worker pool adalah teknik manajemen goroutine dalam *concurrent programming* pada Go. Sejumlah worker dijalankan dan masing-masing memiliki tugas yang sama yaitu menyelesaikan sejumlah jobs.
Dengan metode worker pool ini, maka penggunaan memory dan performansi program akan bisa optimal.
-#### • Database Connection Pool
+#### ◉ Database Connection Pool
*Connection pool* adalah metode untuk manajemen sejumlah koneksi database, agar bisa digunakan secara optimal.
@@ -20,7 +20,7 @@ Connection pool sangat penting dalam kasus operasi data yang berhubungan dengan
Karena pada concurrent programming, beberapa proses akan berjalan bersamaan, maka penggunaan 1 koneksi db akan menghambat proses tersebut. Perlu ada beberapa koneksi database, agar goroutine tidak rebutan objek koneksi database.
-#### • Failover
+#### ◉ Failover
Failover merupakan mekanisme backup ketika sebuah proses gagal. Pada konteks ini, failover mengarah ke proses untuk me-retry operasi insert ketika gagal.
@@ -63,7 +63,7 @@ Jika pembaca ingin menggunakan driver lain, juga silakan.
## D.1.3. Praktek
-#### • Definisi Konstanta
+#### ◉ Definisi Konstanta
Ada beberapa konstanta yang perlu dipersiapkan. Pertama connection string untuk komunikasi ke database server. Sesuaikan value nya dengan yang dipergunakan.
@@ -101,7 +101,7 @@ Terakhir, siapkan variabel untuk menampung data header dari pembacaan CSV nanti.
var dataHeaders = make([]string, 0)
```
-#### • Fungsi Buka Koneksi Database
+#### ◉ Fungsi Buka Koneksi Database
Buat fungsi untuk buka koneksi database, yg dikembalikan objek database kembalian fungsi `sql.Open()`.
@@ -131,7 +131,7 @@ O iya jangan lupa untuk import driver nya.
import _ "github.com/go-sql-driver/mysql"
```
-#### • Fungsi Baca CSV
+#### ◉ Fungsi Baca CSV
Buka file CSV, lalu gunakan objek file untuk membuat objek CSV reader baru.
@@ -149,7 +149,7 @@ func openCsvFile() (*csv.Reader, *os.File, error) {
}
```
-#### • Fungsi Menjalankan Workers
+#### ◉ Fungsi Menjalankan Workers
Ok, sekarang kita mulai masuk ke aspek konkurensi dari pembahasan ini. Siapkan fungsi yang isinya men-dispatch beberapa goroutine sejumlah `totalWorker`.
@@ -181,7 +181,7 @@ Fungsi `doTheJob()` yang nantinya kita buat, isinya adalah operasi insert data k
Idealnya di akhir aplikasi akan terjadi pemanggilan `wg.Done()` sejumlah 1 juta karena ada 1 juta jobs.
-#### • Fungsi Baca CSV dan Pengiriman Jobs ke Worker
+#### ◉ Fungsi Baca CSV dan Pengiriman Jobs ke Worker
Proses pembacaan CSV, apapun metodenya pasti yang dijalankan adalah membaca data dari line ke line dari baris paling bawah.
@@ -221,7 +221,7 @@ Setelah proses baca data selesai, channel di close. Karena pengiriman dan peneri
Jika blok kode perulangan dalam fungsi di atas selesai, maka sudah tidak ada lagi operasi kirim terima data, maka kita close channelnya.
-#### • Fungsi Insert Data ke Database
+#### ◉ Fungsi Insert Data ke Database
```go
func doTheJob(workerIndex, counter int, db *sql.DB, values []interface{}) {
@@ -281,7 +281,7 @@ O iya, mengenai kode untuk manajemen db connection poll mana ya? sepertinya tida
Btw, di atas juga ada satu fungsi lagi, `generateQuestionsMark()`, gunanya untuk membantu pembentukan query insert data secara dinamis.
-#### • Fungsi Main
+#### ◉ Fungsi Main
Terakhir, panggil semua fungsi yang sudah dibuat pada main.
diff --git a/content/README.md b/content/README.md
index 412444a01..3d385ec2a 100644
--- a/content/README.md
+++ b/content/README.md
@@ -23,7 +23,7 @@ Ada total sekitar 120 chapter yang dibahas dalam ebook ini. Kumpulan chap
-Versi e-book: **((VERSION))**, dan versi Go **1.20**.
+Versi e-book: **((VERSION))**, dan versi Go **1.22**.
## Download File E-book (pdf, epub, mobi)
diff --git a/content/SUMMARY.md b/content/SUMMARY.md
index b2530b66f..46cf1a6d7 100644
--- a/content/SUMMARY.md
+++ b/content/SUMMARY.md
@@ -19,7 +19,7 @@
* [A.1. Belajar Golang](1-berkenalan-dengan-golang.md)
* [A.2. Instalasi Golang (Stable & Unstable)](2-instalasi-golang.md)
* [A.3. Setup Go Modules](A-setup-go-project-dengan-go-modules.md)
- * [A.4. Setup GOPATH Dan Workspace](A-gopath-dan-workspace.md)
+ * [A.4. Setup GOPATH dan Workspace](A-gopath-dan-workspace.md)
* [A.5. Instalasi Editor](A-instalasi-editor.md)
* [A.6. Go Command](A-go-command.md)
* [A.7. Program Pertama: Hello World](A-hello-world.md)
@@ -43,7 +43,7 @@
* [A.25. Method](A-method.md)
* [A.26. Properti Public dan Private (Exported vs Unexported)](A-properti-public-dan-private.md)
* [A.27. Interface](A-interface.md)
- * [A.28. Interface Kosong (Any)](A-interface-kosong.md)
+ * [A.28. Any / interface{} / Interface Kosong](A-interface-kosong.md)
* [A.29. Reflect](A-reflect.md)
* [A.30. Goroutine](A-goroutine.md)
* [A.31. Channel](A-channel.md)
@@ -60,7 +60,7 @@
* [A.42. Time Duration](A-time-duration.md)
* [A.43. Konversi Antar Tipe Data](A-data-type-conversion.md)
* [A.44. Fungsi String](A-strings.md)
- * [A.45. Regex](A-regex.md)
+ * [A.45. Regexp](A-regex.md)
* [A.46. Encode - Decode Base64](A-encoding-base64.md)
* [A.47. Hash Sha1](A-hash-sha1.md)
* [A.48. Arguments & Flag](A-command-line-args-flag.md)
diff --git a/content/images/A_generics_1.png b/content/images/A_generics_1.png
index 02347071b..5b9b82604 100644
Binary files a/content/images/A_generics_1.png and b/content/images/A_generics_1.png differ
diff --git a/content/images/A_generics_2.png b/content/images/A_generics_2.png
index 7a55bf648..7390a978f 100644
Binary files a/content/images/A_generics_2.png and b/content/images/A_generics_2.png differ
diff --git a/examples b/examples
index fb8ec78c8..b460b751e 160000
--- a/examples
+++ b/examples
@@ -1 +1 @@
-Subproject commit fb8ec78c88888c8f7ebf3dedcf7111b626b2689b
+Subproject commit b460b751eab0d168870710e1fcbbc6f9fee9c183