DEV Community

Cover image for Mengelola Kode Dengan Modul Dan Fungsi
Muzhawir Amri
Muzhawir Amri

Posted on

Mengelola Kode Dengan Modul Dan Fungsi

Daftar Isi

Pendahuluan

Elixir adalah bahasa pemrograman fungsional yang mengandalkan fungsi untuk menangani data yang bersifat tidak dapat diubah (immutability). Fungsi-fungsi ini dikelompokkan ke dalam modul. Modul berfungsi sebagai wadah untuk mengorganisasi fungsi-fungsi terkait dan memudahkan pengelolaan dan penggunaan kode. Setiap modul dapat berisi satu atau lebih fungsi yang dapat dipanggil untuk berbagai operasi.

Modul

Modul di Elixir berfungsi sebagai wadah untuk kumpulan fungsi terkait, mirip dengan namespace dalam bahasa pemrograman lain. Setiap fungsi dalam Elixir harus didefinisikan di dalam modul, dan modul didefinisikan menggunakan konvensi penulisan CamelCase.

Sebagai contoh, Elixir menyediakan modul standar IO untuk operasi I/O. Fungsi puts berada di dalam modul IO yang kita dapat digunakan untuk menampilkan pesan ke layar:

iex(1)> IO.puts("Halo Dunia!") 
Halo Dunia! 
:ok 
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah sebagai berikut:

  1. Memanggil fungsi puts dari modul IO.
  2. Fungsi IO.puts mencetak "Halo Dunia!" ke layar.
  3. Nilai kembalian dari IO.puts adalah :ok.

Kode tersebut menunjukkan bahwa untuk memanggil fungsi dari modul kita menggunakan sintaks NamaModul.nama_fungsi(argumen_1, argumen_n).

Mendefinisikan Modul Sendiri

Untuk mendefinisikan modul sendiri, kita menggunakan defmodule, dan fungsi di dalamnya didefinisikan menggunakan def, berikut contohnya:

Listing: Mendefinisikan sebuah modul (geometry.ex)

defmodule Geometry do 
  def rectangle_area(length, width) do 
    length * width
  end 
end 
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. Mendefinisikan sebuah modul bernama Geometry.
  2. Di dalam modul, kita mendefiniskan sebuah fungsi bernama rectangle_area dan diakhiri dengan ekspresi end untuk menandakan akhir dari tubuh fungsi.
  3. Modul ditutup dengan ekspresi end.

Modul ini dapat diuji langsung dalam iex dengan menginterpretasikan berkasnya:

$ iex geometry.ex 

iex(1)> Geometry.rectangle_area(6, 7) 
42 
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah sebagai berikut:

  1. Menjalankan iex dan memuat modul Geometry dari file geometry.ex ke dalam sesi interactive shell.
  2. Memanggil fungsi rectangle_area dari modul Geometry dengan argumen 6 dan 7.
  3. Hasil dari fungsi tersebut adalah 42.

Dengan mendefinisikan modul ini, kita dapat memuatnya ke dalam sesi shell Elixir dan menggunakan fungsinya untuk menghitung luas persegi panjang.

Dalam source code Elixir, modul biasanya didefinisikan dalam satu file dengan ekstensi .ex, tetapi satu file dapat berisi lebih dari satu modul.

defmodule Geometry do
  defmodule Rectangle do
    ...
  end
end
Enter fullscreen mode Exit fullscreen mode

Dalam Elixir, modul yang berada di dalam modul lain bisa diakses dengan notasi seperti Geometry.Rectangle. Titik (.) dalam nama modul tidak memiliki makna khusus, melainkan hanya digunakan sebagai karakter pemisah dalam nama modul. Ketika kode dikompilasi, tidak ada informasi tentang hubungan hierarki antara modul yang disimpan.

Penggunaan notasi hierarki ini berguna untuk mengatur modul dengan cara yang lebih terstruktur dan memudahkan navigasi kode. Misalnya, jika ada dua pustaka berbeda, satu untuk encoding JSON dan satu lagi untuk encoding XML, keduanya mungkin memiliki modul dengan nama Encoder. Jika kedua pustaka ini digunakan dalam proyek yang sama, nama modul yang sama akan menyebabkan konflik. Namun, jika nama modul diubah menjadi Json.Encoder dan Xml.Encoder, konflik nama dapat dihindari.

Oleh karena itu, biasanya nama aplikasi atau pustaka digunakan sebagai awalan untuk menghindari bentrok nama dan membuat organisasi kode lebih jelas.

Fungsi

Fungsi merupakan bagian penting dari setiap modul di Elixir. Konvensi penamaan fungsi di Elixir mengikuti aturan serupa dengan variabel:

  • Nama fungsi harus dimulai dengan huruf kecil atau garis bawah (_).
  • Setelah karakter pertama, nama fungsi dapat terdiri dari kombinasi huruf, angka, dan garis bawah.
  • Fungsi dapat diakhiri dengan tanda tanya (?) untuk menandai bahwa fungsi tersebut mengembalikan nilai boolean (true atau false), atau tanda seru (!) untuk menunjukkan bahwa fungsi tersebut mungkin memiliki efek samping atau dapat memicu kesalahan.

Pentingnya Simbol Dalam Penamaan Fungsi

Fungsi di Elixir dapat diakhiri dengan tanda tanya (?) atau tanda seru (!). Tanda tanya (?) digunakan untuk fungsi yang mengembalikan nilai boolean (true atau false), yang mencerminkan pertanyaan—apakah suatu kondisi terpenuhi. Sebaliknya, tanda seru (!) digunakan untuk fungsi yang mungkin memicu kesalahan runtime, menandakan potensi efek samping yang serius atau kesalahan yang tidak terduga. Mengikuti konvensi ini dapat membantu kita menjaga konsistensi dan memudahkan pemahaman fungsi bagi pengembang lain.

Mendefinisikan Fungsi

Untuk mendefinisikan fungsi dalam Elixir, kita menggunakan makro def di dalam blok defmodule. Berikut adalah struktur dasar untuk mendefinisikan fungsi:

defmodule Geometry do
  def rectangle_area(a, b) do 
    # tubuh fungsi ❷
  end
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. def rectangle_area(a, b) do mendefinisikan fungsi rectangle_area dengan dua argumen, a dan b.
  2. Tubuh fungsi adalah bagian antara blok do dan end, di sinilah kode fungsi ditulis.

Pada contoh di atas, rectangle_area adalah fungsi yang menghitung area dari sebuah persegi panjang. Penggunaan def diikuti oleh nama fungsi dan argumen yang dibutuhkan, serta diapit oleh blok do...end yang menandakan awal dan akhir dari definisi fungsi.

Jika sebuah fungsi tidak memerlukan argumen, tanda kurung dapat dihilangkan dalam definisinya, memperjelas bahwa fungsi tersebut tidak memerlukan input argumen:

defmodule Program do
  def run do
    # Implementasi fungsi
  end
end
Enter fullscreen mode Exit fullscreen mode

Nilai Pengembalian Fungsi

Di Elixir, konsep nilai pengembalian fungsi berbeda dari beberapa bahasa pemrograman lain yang menggunakan kata kunci return secara eksplisit. Di bahasa ini, setiap fungsi secara otomatis mengembalikan nilai dari ekspresi terakhir yang dievaluasi di dalamnya.

Mari kita lihat contoh fungsi yang menghitung luas persegi panjang berikut:

defmodule Geometry do
  def rectangle_area(a, b) do
    a * b
  end
end
Enter fullscreen mode Exit fullscreen mode

Dalam contoh di atas, ekspresi a * b adalah ekspresi terakhir yang dievaluasi di dalam fungsi rectangle_area. Nilai dari ekspresi ini secara otomatis dikembalikan sebagai hasil dari fungsi tersebut, tanpa memerlukan kata kunci return.

Mendefinisikan Fungsi Dalam Satu Baris

Jika tubuh fungsi hanya terdiri dari satu ekspresi dan tidak memerlukan logika tambahan, kita dapat menggunakan sintaks alternatif yang lebih ringkas:

defmodule Geometry do
  def rectangle_area(a, b), do: a * b
end
Enter fullscreen mode Exit fullscreen mode

Sintaks ini efektif untuk fungsi sederhana yang langsung mengembalikan hasil dari satu ekspresi, menjadikan kode lebih bersih dan mudah untuk dibaca. Tentu saja jika kita menjalankannya di interactive shell hasilnya sama dengan versi fungsi yang sebelumnya.

Memanggil Fungsi Antar Modul Yang Berbeda

Untuk memanggil fungsi dari modul lain, gunakan nama modul diikuti dengan nama fungsinya:

iex(1)> Geometry.rectangle_area(3, 2) 
6

iex(2)> area = Geometry.rectangle_area(3, 2) 
6

iex(3)> area 
6
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. Memanggil fungsi rectangle_area dari modul Geometry.
  2. Fungsi dapat disimpan dalam variabel, sesuai dengan prinsip first-class function.
  3. Memverifikasi nilai variabel area, yang merupakan hasil dari fungsi Geometry.rectangle_area.

Memanggil Fungsi Dalam Modul Yang Sama

Ketika sebuah fungsi dipanggil dari modul yang sama, kita tidak perlu menyertakan nama modul di depannya. Ini mengurangi redundansi dan mempertahankan kejelasan kode:

defmodule Geometry do
  def rectangle_area(a, b) do 
    a * b
  end

  def square_area(a) do 
    rectangle_area(a, a)
  end
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. Mendefinisikan fungsi rectangle_area.
  2. Mendefinisikan fungsi square_area, yang memanggil fungsi rectangle_area tanpa menyertakan nama modulnya karena berada dalam modul yang sama.

Praktek ini umum dalam Elixir untuk meningkatkan keterbacaan dan memudahkan manajemen kode internal modul. Memahami cara memanggil fungsi dengan benar sangat penting untuk mengelola proyek Elixir yang besar, di mana modul dan fungsi bisa sangat banyak dan kompleks.

Menggunakan Pipe Operator

Pipe Operator |> di Elixir sangat berguna untuk menyusun fungsi secara berantai, ini membuat alur kode kita lebih mudah dibaca. Operator ini mengalirkan hasil dari satu fungsi sebagai argumen pertama ke fungsi berikutnya, seperti dalam contoh berikut:

iex(5)> -5 |> abs() |> Integer.to_string() |> IO.puts()
5
Enter fullscreen mode Exit fullscreen mode

Dalam contoh di atas, nilai -5 diubah menjadi angka positif dengan fungsi abs(), kemudian diubah menjadi string oleh fungsi Integer.to_string(), dan terakhir dicetak ke terminal dengan fungsi IO.puts(). Penggunaan pipe operator memudahkan pemahaman alur data melalui fungsi-fungsi ini.

Saat dikompilasi, kode di atas akan diubah menjadi fungsi yang disusun menggunakan teknik staircasing:

iex(6)> IO.puts(Integer.to_string(abs(-5)))
5
Enter fullscreen mode Exit fullscreen mode

Untuk meningkatkan keterbacaan kode, kita juga bisa menyusun penggunaan pipe operator dalam beberapa baris:

-5
|> abs()
|> Integer.to_string()
|> IO.puts()
Enter fullscreen mode Exit fullscreen mode

Penyusunan fungsi seperti ini membantu melacak transformasi data langkah demi langkah, dengan kode dibaca dari kiri ke kanan atau dari atas ke bawah.

Ariti Fungsi

Ariti dalam Elixir merujuk pada jumlah argumen yang diterima oleh sebuah fungsi. Fungsi di Elixir dapat diidentifikasi tidak hanya berdasarkan nama fungsi dan nama modulnya, tetapi juga berdasarkan ariti—jumlah argumen yang diterima.

Sebagai contoh, lihat fungsi area dalam modul Rectangle:

defmodule Rectangle do
  def area(a, b) do
    a * b 
  end
end
Enter fullscreen mode Exit fullscreen mode

Fungsi Rectangle.area memiliki dua argumen, sehingga aritinya adalah 2. Dalam dokumentasi dan kode Elixir, fungsi ini sering dirujuk sebagai Rectangle.area/2.

Pentingnya Ariti

Kenapa ariti itu penting? Di Elixir, dua fungsi dengan nama yang sama tetapi jumlah argumen yang berbeda dianggap sebagai fungsi yang berbeda. Ariti membantu membedakan fungsi-fungsi tersebut dan memastikan mereka dipanggil dengan cara yang benar. Mari kita lihat contohnya:

Listing: Ariti Fungsi (arity.ex)

defmodule Rectangle do
  def area(a), do: area(a, a)

  def area(a, b), do: a * b
end
Enter fullscreen mode Exit fullscreen mode

Pada contoh kode di atas, modul Rectangle memiliki dua fungsi dengan nama yang sama, yaitu area. Namun, fungsi ini memiliki jumlah argumen yang berbeda: satu dengan 1 argumen (ariti 1) dan lainnya dengan 2 argumen (ariti 2). Fungsi pertama disebut Rectangle.area/1, sedangkan fungsi kedua disebut Rectangle.area/2. Ini berarti kedua fungsi tersebut dianggap berbeda karena jumlah argumen yang mereka terima.

Sekarang, mari kita coba kedua fungsi tersebut. Muat modul di atas ke dalam iex, lalu panggil fungsinya seperti ini:

iex(1)> Rectangle.area(5)
25

iex(2)> Rectangle.area(5,6)
30
Enter fullscreen mode Exit fullscreen mode

Seperti yang terlihat, kedua fungsi ini berfungsi dengan cara yang berbeda. Nama fungsinya sama, tetapi jumlah argumennya berbeda, sehingga dianggap sebagai dua fungsi yang berbeda, masing-masing dengan implementasinya sendiri.

Biasanya, tidak masuk akal jika dua fungsi dengan nama yang sama memiliki implementasi yang sepenuhnya berbeda. umumnya, fungsi dengan ariti yang lebih rendah mendelegasikan tugasnya ke fungsi dengan ariti yang lebih tinggi dengan memberikan beberapa argumen default. Dalam implementasi di atas, Rectangle.area/1 mendelegasikan tugasnya ke Rectangle.area/2.

Mari kita lihat contoh lainnya:

Listing: Ariti Fungsi (anothe_arity.ex)

defmodule Calculator do 
    def add(a), do: add(a, 0) 
    def add(a, b), do: a + b 
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. Fungsi Calculator.add/1 mendelegasikan tugasnya ke fungsi Calculator.add/2 dengan memberikan argumen default 0 untuk argumen kedua.
  2. Fungsi Calculator.add/2 melakukan operasi penjumlahan sesuai dengan implementasinya.

Fungsi Dengan Parameter Default

Elixir mendukung parameter default yang memungkinkan kita mengurangi jumlah fungsi yang perlu kita ditulis:

defmodule Calculator do
  def add(a, b \\ 0), do: a + b   
end
Enter fullscreen mode Exit fullscreen mode

Dalam kode di atas, fungsi Calculator.add/2 mendefinisikan nilai default 0 untuk argumen b.

Penggunaan parameter default pada suatu fungsi dapat menghasilkan variasi fungsi dengan ariti yang berbeda dari definisi tunggal. Sebagai contoh:

defmodule MyModule do
  def fun(arg1, arg2 \\ 1, arg3, arg4 \\ 2) do   
    arg1 + arg2 + arg3 + arg4 
  end
end
Enter fullscreen mode Exit fullscreen mode

Dalam kode di atas, fungsi MyModule.fun/4 secara otomatis menghasilkan beberapa versi fungsi berdasarkan kombinasi argumen yang diterima dan argumen default:

def fun(arg1, arg3), do: fun(arg1, 1, arg3, 2)

def fun(arg1, arg2, arg3), do: fun(arg1, arg2, arg3, 2)

def fun(arg1, arg2, arg3, arg4), do: arg1 + arg2 + arg3 + arg4
Enter fullscreen mode Exit fullscreen mode

Karena ariti membedakan fungsi-fungsi dengan nama yang sama, tidak memungkinkan untuk memiliki fungsi yang menerima jumlah argumen variabel. Elixir tidak memiliki padanan dari ... di bahasa C atau arguments di JavaScript, sehingga membantu menjaga kejelasan dalam definisi dan penggunaan fungsi.

Visibilitas Fungsi

Dalam Elixir, visibilitas fungsi diatur dengan dua makro: def dan defp. Makro ini membedakan antara fungsi yang dapat diakses secara publik dan fungsi yang bersifat privat.

Fungsi Publik Dan Privat

  • Fungsi Publik: Didefinisikan menggunakan def, fungsi publik dapat dipanggil dari modul lain atau dari sesi interactive shell. Ini memungkinkan fungsi tersebut diakses secara luas.
  • Fungsi Privat: Didefinikan menggunakan defp, fungsi privat hanya dapat dipanggil dari dalam modul tempat mereka didefinisikan. Fungsi privat mendukung fungsi publik dalam modul itu sendiri atau menyelesaikan tugas internal yang tidak perlu diungkapkan ke luar.

Mari kita lihat contoh berikut implementasi dari kedua jenis fungsi ini:

Listing: Fungsi Publik Dan Privat (public_private_function.ex)

defmodule TestPrivate do
  def double(a) do   
    sum(a, a)   
  end

  defp sum(a, b) do   
    a + b
  end 
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. Mendefinisikan fungsi publik double/1.
  2. Dalam tubuh fungsi double/1, memanggil fungsi privat sum/2.
  3. Mendefinisikan fungsi privat sum/2.

Dalam kode di atas, modul TestPrivate mendefinisikan fungsi publik double/2 yang mengunakan fungsi privat sum/2 untuk menghitung dua kali nilai dari argumen yang diberikan. Fungsi sum/2 bersifat privat dan hanya digunakan sebagai pembantu untuk fungsi doule/2.

Menguji Visibilitas Fungsi

Ketika kita mencoba memanggil fungsi ini dari interactive shell Elixir, kita akan melihat perbedaan dalam visibilitas:

iex(1)> TestPrivate.double(3)
6  # Fungsi publik `double` berhasil dipanggil

iex(2)> TestPrivate.sum(3, 4)
** (UndefinedFunctionError) function TestPrivate.sum/2 is private
...
Enter fullscreen mode Exit fullscreen mode

Pada baris kedua interactive shell, saat kita memanggil fungsi sum/2 maka akan muncul pesan UndefinedFunctionError, ini terjadi karena fungsi ini bersifat privat dan tidak dapat diakses dari luar modulnya.

Pentingnya Enkapsulasi

Pemisahan antara fungsi publik dan privat penting untuk konsep enkapsulasi dalam pemrograman fungsional. Dengan membatasi akses ke fungsi-fungsi tertentu, modul dapat menyembunyikan detail implementasi internalnya, memungkinkan pemanggil fokus pada fungsionalitas modul tanpa perlu memikirkan implementasinya. Ini membantu menjaga kebersihan dan pemeliharaan kode.

Fungsi privat juga mengurangi kemungkinan penggunaan yang tidak tepat oleh bagian kode lain, yang dapat menyebabkan bug sulit ditelusuri. Penggunaan defp untuk fungsi privat memperjelas bahwa fungsi tersebut hanya dimaksudkan untuk digunakan secara internal dalam modul.

Import Dan Alias

Dalam Elixir, untuk memudahkan dan mempersingkat kode saat bekerja dengan modul lain, kita dapat menggunakan dua cara yaitu menggunakan import dan alias. Kedua ini memungkinkan kita untuk merujuk fungsi atau modul dengan cara yang lebih sederhana dan efisien.

Menggunakan Import

Ketika kita mengimpor modul, kita dapat memangil fungsi publiknya tanpa perlu menyertakan nama modulnya setiap kali:

defmodule MyModule do
  import IO 

  def my_function do
    puts "Calling imported function." 
  end
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas:

  1. Mengimpor modul IO ke dalam modul MyModule.
  2. Memanggil fungsi puts tanpa harus menulis prefix IO.

Dengan mengimpor modul IO, kita bisa langsung menggunakan fungsi puts tanpa perlu menuliskan IO.puts. Ini sangat membantu, terutama ketika kita sering menggunakan fungsi dari modul yang diimpor dalam kode yang kita tulis.

Menggunakan Alias

alias memungkinkan kita memberikan nama alternatif (alias) untuk modul, yang sangat berguna jika modul tersebut memiliki nama panjang atau jika kita ingin menggunakan nama yang lebih singkat atau deskriptif:

defmodule MyModule do
  alias IO, as: MyIO 

  def my_function do
    MyIO.puts("Calling imported function.") 
  end
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas:

  1. Membuat alias MyIO untuk modul IO.
  2. Memanggil fungsi menggunakan alias MyIO alih-alih IO.

Di sini, kita memberikan alias MyIO untuk modul IO, sehingga kita dapat menggunakan MyIO.puts alih-alih IO.puts, yang dapat meningkatkan kejelasan kode tergantung pada konteks pemrograman kita.

Kita juga bisa menggunakan alias tanpa opsi as:, yang akan menggunakan bagian terakhir dari nama modul sebagai alias. Berikut contohnya:

defmodule MyModule do
  alias Geometry.Rectangle 

  def my_function do
    Rectangle.area(...) 
  end
end
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas:

  1. Membuat alias Rectangle untuk modul Geometry.Rectangle.
  2. Memanggil fungsi area menggunakan alias Rectangle.

Umumnya, developer Elixir memberikan alias kepada modul hanya dengan bagian terakhir dari namanya untuk menyederhanakan referensi.

Pentingnya Import Dan Alias

  1. Kemudahan Akses: Mengurangi kebutuhan untuk menuliskan nama modul secara lengkap setiap kali memanggil fungsi, yang bisa sangat membantu dalam modul yang sering digunakan atau memiliki nama panjang.
  2. Menghindari Redundansi: Membantu menghindari pengulangan nama modul yang tidak perlu dan membuat kode lebih ringkas.
  3. Kejelasan dan Konteks: Alias dapat memberikan konteks tambahan atau mengklarifikasi penggunaan modul tertentu dalam kode kita, terutama jika nama modul asli kurang jelas atau terlalu umum.

Penggunaan import dan alias adalah praktek pengembangan yang baik dalam Elixir karena membantu mempertahankan kode yang terorganisir, mudah dipahami, dan mudah dipelihara.

Atribut Modul

Atribut modul dalam Elixir memainkan peran penting dalam pengaturan konstanta pada waktu kompilasi (compile-time) dan penyediaan informasi yang dapat diakses selama runtime. Keduanya memperkaya modularitas dan dokumentasi kode.

Kontanta Pada Waktu Kompilasi

Atribut modul dapat digunakan sebagai konstanta pada waktu kompilasi:

defmodule Circle do
  @pi 3.14159 

  def area(r), do: r * r * @pi 
  def circumference(r), do: 2 * r * @pi
end

iex(1)> Circle.area(1) # Hasil: 3.14159
iex(2)> Circle.circumference(1) # Hasil: 6.28318
Enter fullscreen mode Exit fullscreen mode

Penjelasan kode di atas adalah:

  1. Mendefinisikan atribut @pi.
  2. Menggunakan atribut @pi sebagai konstanta dalam fungsi.

Dalam contoh kode di atas, @pi didefinisikan sebagai konstanta dalam modul Circle. Fungsi dalam modul ini menggunakan @pi yang nilainya ditetapkan selama kompilasi.

Informasi Yang Dapat Diakses Saat Runtime

Atribut modul juga bisa menyimpan dokumentasi yang dapat diakses selama runtime:

defmodule Circle do
  @moduledoc "Menyediakan fungsi dasar untuk operasi lingkaran"

  @pi 3.14159

  @doc "Menghitung luas lingkaran"
  def area(r), do: r * r * @pi

  @doc "Menghitung keliling lingkaran"
  def circumference(r), do: 2 * r * @pi
end
Enter fullscreen mode Exit fullscreen mode

Penggunaan @moduledoc dan @doc memberikan konteks dan penjelasan tentang modul dan fungsinya yang dapat diakses melalui alat bantu seperti iex:

iex(1)> h Circle       # Menampilkan dokumentasi untuk modul Circle
iex(2)> h Circle.area  # Menampilkan dokumentasi untuk fungsi area
Enter fullscreen mode Exit fullscreen mode

Mendaftarkan Atribut

Atribut modul tidak hanya terbatas pada dokumentasi. Kita bisa mendefinisikan atribut khusus yang digunakan dalam logika internal modul atau untuk keperluan refleksi:

defmodule Example do
  @my_attribute "Important data"
  def get_attribute, do: @my_attribute
end
Enter fullscreen mode Exit fullscreen mode

Di sini, @my_attribute menunjukkan bagaimana atribut dapat ditetapkan dan diakses dalam modul yang sama.

Atribut modul dalam Elixir sangat fleksibel dan digunakan untuk berbagai keperluan, mulai dari mendefinisikan konstanta waktu kompilasi hingga menyimpan metadata yang bisa diakses selama runtime. Ini merupakan bagian penting dari struktur kode dalam Elixir yang meningkatkan modularitas, kejelasan, dan kemudahan penggunaan. Informasi lebih lanjut mengenai penggunaan atribut modul dapat ditemukan di dokumentasi Elixir.

Typespecs

Spesifikasi tipe atau typespecs di Elixir memungkinkan kita untuk mendefinisikan tipe data yang diterima oleh argumen dan dikembalikan oleh fungsi. Ini membantu dalam dokumentasi serta analisis statis kode melalui alat seperti Dialyzer, dan juga meningkatkan keamanan kode dengan memverifikasi kecocokan tipe data secara statis sebelum runtime.

Mari kita perhatikan bagaimana typespecs diimplementasikan dalam modul Circle yang menyediakan fungsi operasi dasar untuk lingkaran:

defmodule Circle do
  @pi 3.14159

  @spec area(number()) :: number() 
  def area(r), do: r * r * @pi

  @spec circumference(number()) :: number() 
  def circumference(r), do: 2 * r * @pi
end
Enter fullscreen mode Exit fullscreen mode

Dalam contoh kode di atas, kita menggunakan atribut @spec untuk menunjukkan bahwa kedua fungsi, area dan circumference, menerima dan mengembalikan tipe data number(). Ini memberikan kejelasan bahwa fungsi-fungsi ini bekerja dengan angka dan menghasilkan angka, ini membantu memastikan bahwa tipe data yang diproses sesuai dengan yang diharapkan.

Typespecs adalah cara untuk memberikan informasi tentang jenis data yang diterima dan dikembalikan oleh fungsi. Ini berfungsi mirip dengan komentar yang menjelaskan bagaimana fungsi seharusnya digunakan. Dengan typespecs, kita bisa mendokumentasikan fungsi dengan lebih jelas, sehingga lebih mudah bagi pengembang lain untuk memahami kode kita.

Elixir adalah bahasa pemrograman dinamis, yang berarti bahwa jenis data tidak selalu diperiksa secara ketat selama penulisan kode. Typespecs membantu mengatasi hal ini dengan memberikan penjelasan eksplisit tentang jenis data yang digunakan, yang bisa sangat berguna ketika bekerja dengan kode yang kompleks atau saat berkolaborasi dengan orang lain.

Sebagai contoh, lihat typespec untuk fungsi Elixir List.insert_at/3:

@spec insert_at(list, integer, any) :: list
Enter fullscreen mode Exit fullscreen mode

Typespec ini menunjukkan bahwa fungsi insert_at menerima tiga argumen: sebuah list, sebuah integer yang menunjukkan posisi, dan sebuah nilai dari tipe apa pun. Fungsi ini kemudian mengembalikan list baru. Meskipun tanpa melihat implementasi atau dokumentasi, typespec ini memberikan kita pemahaman yang jelas tentang apa yang dilakukan fungsi tersebut dan bagaimana cara kerjanya.

Typespecs meningkatkan kejelasan dan membantu dalam memeriksa kesalahan pada waktu kompilasi, membuat kode lebih mudah dipahami dan dikelola. Untuk informasi lebih lanjut tentang typespecs, kita dapat membacanya di dokumentasi Elixir.

Komentar

Dalam Elixir, komentar merupakan bagian penting dari kode yang membantu menjelaskan fungsi, tujuan, atau alasan di balik potongan kode tertentu, serta untuk menonaktifkan sementara kode tanpa menghapusnya.

Elixir hanya mendukung komentar satu baris, di mana semua teks setelah tanda pagar (#) pada baris tersebut akan diabaikan oleh compiler. Jika perlu mengomentari beberapa baris, setiap baris harus dimulai dengan #:

# Ini adalah komentar yang menjelaskan kode
a = 3.14 # Ini juga komentar yang mengikuti kode

# Ini adalah cara untuk mengomentari beberapa baris
# dengan menambahkan # di setiap baris.
# Semua baris dengan # akan dianggap komentar.
Enter fullscreen mode Exit fullscreen mode

Referensi

  • Juric, S. (2024). Elixir in Action (3rd ed.). Manning Publications.

Top comments (0)