Modüller

Some contents of this translation may be outdated.
Several minor changes were applied to the original lesson since the last update.

Bu zaman kadar ki derslerimiz de fonksiyonları aynı dosya ve alanda tanımladık. In this lesson we’re going to cover how to group functions and define a specialized map in a struct in order to organize our code more efficiently.

İçerik

Modüller

Modüller, fonksiyonları bir isim namespace da organize etmemizi sağlar. Bunlara ek olarak fonksiyonlar dersinde. adlandırılmış ve özel fonksiyonları tanımlamamıza izin verir.

Basit bir örneğe bakalım:

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

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

Elixir’de kodunuzu modüllerin içine eklemeniz namespacesinizi daha efektif kullanmanızı sağlar. Elixir

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."

Modül Nitelikleri (Attributes)

Elixir’de nitelikleri sıklıkla kullanılır. Basit bir örneğe bakalım:

defmodule Example do
  @greeting "Hello"

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

Elixir’de bazı niteliklerin özel olarak ayrıldığını belirmekte fayda var. En yaygın 3 tanesi:

  • moduledoc — Geçerli modüle ait dokümanlar.
  • doc — Fonksiyon ve makrolar için dokümanlar
  • behaviour — OTP veya kullanıcı tanımlı davranış için kullanma.

Yapılarlar (Structs)

Yapılar anahtar kelime ve varsayılan değerlerinden oluşan özel haritalardır. Yapının adını alacağı bir modül içne tanımlanmalıdır. Modül içinde yapı tek başına tanımlanması yaygın bir kullanımdır.

Yapı tanımlamak için defstruct ile birlikte anahtar kelime listesi ve varsayılan değerleri ile birlikte kullanırız :

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

Yapılar yaratalım:

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

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

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

Yapıyı tıpkı bir harita gibi güncelleyebiliriz:

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

En önemlisi haritalarla yapılara erişebilirsiniz:

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

Birleştirme (Composition)

Artık modülleri ve ve yapıları nasıl oluşturacağımız biliyoruz, Birleştirme yolu ile farklı fonksiyonellikleri nasıl ekleyeceğimizi öğrenelim. Elixir diğer modüllerle etkileşime girmek için farklı yöntemler sunar.

alias

Modülere takma adlar tanımlamamıza izin veriri ve bu Elixir’de sıkça kullanılır:

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

# Takma ad olmadan

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

Mükerrer takma ad varsa veya tamamen farklı bir ad verilmek isteniyorsa :as seçeneği kullanıla bilinir:

defmodule Example do
  alias Sayings.Greetings, as: Hi

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

Aynı anda birden fazla modüle takma ad vermekte de mümkündür:

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

import

If we want to import functions and macros rather than aliasing the module we can use import/: Eğer takma ad kullanmak yerine fonksiyon ve makroları eklemek isterseniz import/ kullana biliriniz:

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

Filtreleme

Varsayılana olarak tüm fonksiyon ve makrolar içeri aktarılır anacak :only ve :except kullanarak filtreleye bilirsiniz.

Belirli fonksiyonları ve makroları aktarmak için :only ve :except kullanırken name/arity (argüman sayısın) kullanmamız gerekiyor. Şimdi last/1 fonksiyonunu içe aktararak başlayalım:

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

Eğer last/1 dışında her şeyi eklemek istiyorsak:

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

Name/arity’e ek olarak sadece fonksiyon yada makroları çağırmak için 2 adet :functions ve :macros atomları bulunmaktadır:

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

require

require/2 çok sık kullanılmasada önemlidir. Bir modül gerekirse onun derlenmesini ve yüklenmesini sağlar. Bir modülün makrolarına erişmeye çalışıtığımızda kolaylık sağlar:

defmodule Example do
  require SuperMacros

  SuperMacros.do_stuff
end

Henüz yüklenmemiş bir makro çağırmaya kalkarsak, Elixir hata mesajı verir.

use

use makrosu ile başka bir modülün mevcut modül tanımımızı değiştirmesini sağlayabiliriz. Kodumuzdaki use fonksiyonunu çağırdığımızda, sağlanan bu modül tarafından tanımlanan __using__/1 callback gerçekleştirilir. __using__/1 makrosunun sonucu modülün tanımının bir parçası haline gelir.

Bunun nasıl işlediğini daha iyi anlamak için örneğe göz atalım:

defmodule Hello do
  defmacro __using__(_opts) do
    quote do
      def hello(name), do: "Hi, #{name}"
    end
  end
end

Burada bir hello/1 fonksiyonu tanımladığımızda __using__/1 bize callback tanımlayan bir Hello modülü oluşturduk. Şimdi kodumuzu denemek için yeni bir kod oluşturalım:

defmodule Example do
  use Hello
end

Kodumuzu IEx’de denersek, hello/1 öğesinin Example modülünde mevcut olduğunu göreceğiz:

iex> Example.hello("Sean")
"Hi, Sean"

Burada ise, Hello modülü üzerinde __using__/1 callback yaptığını görebiliriz ve sonuçta oluşan kod modülize edilmiş olur. Basit bir örnek gösterdik, __using __ / 1’in seçenekleri nasıl desteklediğine bakmak için kodumuzu güncelleyelim. Bunu greeting seçeneği ekleyerek uygulayacağız:

defmodule Hello do
  defmacro __using__(opts) do
    greeting = Keyword.get(opts, :greeting, "Hi")

    quote do
      def hello(name), do: unquote(greeting) <> ", " <> name
    end
  end
end

Example modülünü greeting seçeneğini içerecek şekilde güncelleyelim:

defmodule Example do
  use Hello, greeting: "Hola"
end

IEx komut satırın greeting kullanırsak komut aşağıdaki gibi değişiklik gösterir :

iex> Example.hello("Sean")
"Hola, Sean"

Bu gösterilenler, kullanımın nasıl çalıştığını gösteren en basit örneklerdir, ancak use Elixir araç kutusundaki en güçlü araçlardan biridir. Elixir’i öğrenmeye devam ederken use modülüne göz kulak olun, öğrenmeye devam ederken mutlaka göreceğiniz örneklerden biride use ExUnit.Case, async: true

Not: Meta programalmada kullanılan makrolar : quote, alias, use, require .