Do you want to pick up from where you left of?
Take me there

Composition

Kita tahu dari pengalaman bahwa adalah menyusahkan jika kita mengumpulkan semua fungsi yang kita buat dalam file dan scope (cakupan) yang sama. Dalam pelajaran ini kita akan mengulas bahgaimana mengelompokkan fungsi dan mendefinisikan suatu map khusus yang dikenal sebagai sebuah struct untuk mengorganisasikan code kita secara lebih efisien.

Modul

Modul adalah cara terbaik untuk mengorganisasikan fungsi ke dalam sebuah namespace. Selain mengelompokkan fungsi, modul juga memungkinkan kita mendefinisikan fungsi bernama dan fungsi privat yang kita bahas di pelajaran sebelumnya.

Let’s look at a basic example:

defmodule Example do
  def greeting(name) do
    ~s(Hello #{name}.)
  end
end

iex> Example.greeting "Sean"
"Hello Sean."

Kita bisa membuat modul bertingkat (nested) di Elixir, memungkinkan kita untuk mengelompokkan fungsi-fungsi lebih lanjut:

defmodule Example.Greetings do
  def morning(name) do
    "Good morning #{name}."
  end

  def evening(name) do
    "Good night #{name}."
  end
end

iex> Example.Greetings.morning "Sean"
"Good morning Sean."

Atribut Modul

Atribut modul paling sering digunakan sebagai konstanta di Elixir. Mari lihat contoh sederhana berikut:

defmodule Example do
  @greeting "Hello"

  def greeting(name) do
    ~s(#{@greeting} #{name}.)
  end
end

Penting dicatat bahwa ada atribut yang dicadangkan (reserved) di Elixir. Tiga atribut tercadang (reserved attribute) yang paling umum adalah:

Struct

Struct adalah map yang spesial dengan sekumpulan key yang sudah didefinisikan dan punya nilai default. Struct harus didefinisikan di dalam sebuah modul yang namanya juga menjadi nama struct tersebut. Lazim terjadi bahwa struct tersebut merupakan satu-satunya yang didefinisikan di dalam sebuah modul.

Untuk mendefinisikan sebuah struct kita menggunakan defstruct bersama daftar keyword dari fieldnya dan juga nilai defaultnya:

defmodule Example.User do
  defstruct name: "Sean", roles: []
end

Mari buat beberapa struct:

iex> %Example.User{}
%Example.User<name: "Sean", roles: [], ...>

iex> %Example.User{name: "Steve"}
%Example.User<name: "Steve", roles: [], ...>

iex> %Example.User{name: "Steve", roles: [:manager]}
%Example.User<name: "Steve", roles: [:manager]>

Kita bisa mengubah struct seperti pada map:

iex> steve = %Example.User{name: "Steve"}
%Example.User<name: "Steve", roles: [...], ...>
iex> sean = %{steve | name: "Sean"}
%Example.User<name: "Sean", roles: [...], ...>

Lebih penting lagi, anda bisa mencocokkan struct terhadap map:

iex> %{name: "Sean"} = sean
%Example.User<name: "Sean", roles: [...], ...>

Composition

Setelah kita tahu cara membuat modul dan struct, mari pelajari cara memasukkan fungsionalitas yang sudah ada ke dalamnya melalui komposisi (composition). Elixir memberi kita beragam cara untuk berinteraksi dengan modul lain.

alias

Elixir mengijinkan kita melakukan alias terhadap nama modul, sering dipakai di code Elixir:

defmodule Sayings.Greetings do
  def basic(name), do: "Hi, #{name}"
end

defmodule Example do
  alias Sayings.Greetings

  def greeting(name), do: Greetings.basic(name)
end

# Tanpa alias

defmodule Example do
  def greeting(name), do: Sayings.Greetings.basic(name)
end

If there’s a conflict with two aliases or you just wish to alias to a different name entirely, we can use the :as option:

defmodule Example do
  alias Sayings.Greetings, as: Hi

  def print_message(name), do: Hi.basic(name)
end

It’s possible to alias multiple modules at once:

defmodule Example do
  alias Sayings.{Greetings, Farewells}
end

import

Jika kita ingin mengimpor fungsi dan macro dari modul lain dan bukannya melakukan alias terhadap modul tersebut kita bisa menggunakan import:

iex> last([1, 2, 3])
** (CompileError) iex:9: undefined function last/1
iex> import List
nil
iex> last([1, 2, 3])
3

Filtering

Secara default semua fungsi dan macro diimpor, tetapi kita bisa memfilter menggunakan pilihan :only dan :except.

Untuk mengimpor fungsi dan macro secara spesifik, kita harus memberikan pasangan nama/arity ke :only dan :except. Mari kita awali dengan hanya mengimpor fungsi last/1:

iex> import List, only: [last: 1]
iex> first([1, 2, 3])
** (CompileError) iex:13: undefined function first/1
iex> last([1, 2, 3])
3

Jika kita mengimport semua kecuali last/1 dan mencoba fungsi-fungsi yang sama dengan sebelumnya:

iex> import List, except: [last: 1]
nil
iex> first([1, 2, 3])
1
iex> last([1, 2, 3])
** (CompileError) iex:3: undefined function last/1

Sebagai tambahan pada pasangan nama/arity, ada dua atom spesial :functions dan macros, yang masing-masing hanya mengimpor fungsi dan macro:

import List, only: :functions
import List, only: :macros

require

Walau lebih jarang dipakai require/2 tetaplah penting. Melakukan require pada sebuah modul memastikan bahwa modul itu dikompilasi dan dimuat (load). Ini paling berguna kala kita perlu mengakses makro di sebuah modul:

defmodule Example do
  require SuperMacros

  SuperMacros.do_stuff
end

Jika kita mencoba memanggil sebuah macro yang belum dimuat, Elixir akan menghasilkan error.

use

Menggunakan sebuah modul di konteks saat ini. Ini khususnya berguna ketika sebuah modul perlu melakukan setup. Dengan memanggil use kita mengaktifkan hook __using__ di dalam modul tersebut, memungkinkan modul tersebut mengubah konteks yang ada:

defmodule MyModule do
  defmacro __using__(opts) do
    quote do
      import MyModule.Foo
      import MyModule.Bar
      import MyModule.Baz

      alias MyModule.Repo
    end
  end
end
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!