Fork me on GitHub

Kompozícia

Tento preklad je aktuálny.

Zo skúsenosti vieme, že je dosť nepohodlné, mať všetky funkcie v jedinom súbore. V tejto lekcii sa naučíme, ako funkcie zoskupovať a definovať špeciálny typ mapy, zvaný struct aby sme mohli usporiadať náš kód efektívne.

Obsah

Moduly

Moduly nám umožňujú organizovať funkcie do menných priestorov (namespaces). Definujeme v nich pomenované a privátne funkcie, o ktorých sme si povedali v lekcii o funkciách.

Pozrime sa na jednoduchý príklad:

defmodule Example do
  def pozdrav(meno) do
    ~s(Ahoj #{meno}.)
  end
end

iex> Example.pozdrav "Jano"
"Ahoj Jano."

V Elixire je možné definovať moduly vnorené v iných moduloch, čo nám dovoľuje jemnejšie rozdeľovať funkcionalitu do menných priestorov:

defmodule Example.Pozdravy do
  def rano(meno) do
    "Dobré ráno #{meno}."
  end

  def vecer(meno) do
    "Dobrý večer #{meno}."
  end
end

iex> Example.Pozdravy.rano "Jano"
"Dobré ráno Jano."

Atribúty modulov

Atribúty sa v Elixirových moduloch najčastejšie používajú ako konštanty:

defmodule Example do
  @pozdrav "Ahoj"

  def pozdrav(meno) do
    ~s(#{@pozdrav} #{meno}.)
  end
end

Je dôležité zapamätať si, že v Elixire existujú vyhradené modulové atribúty. Najbežnejšie tri sú tieto:

Structs

Structs sú špeciálny typ máp s definovanými kľúčmi a ich východiskovými hodnotami. Struct musí byť definovaný v module, z ktorého získa svoj názov. Je bežné, že struct je jediná vec definovaná v module.

Na definovanie structu používame kľúčové slovo defstruct nasledované zoznamom kľúčových slov a ich východiskových hodnôt:

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

Teraz si vytvorme niekoľko príkladov tohto structu:

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]}

Struct môžeme meniť rovnako, ako mapu:

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]}

Veľmi dôležitou vlastnosťou structov je, že ich môžeme pattern matchovať s mapami:

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

Skladanie modulov

Teraz, keď už dokážeme vytvárať vlastné moduly, je načase naučiť sa, ako v nich používať funkcie definované v iných moduloch. Elixir nám na tento účel poskytuje niekoľko odlišných kompozičných mechanizmov:

alias

Dovoľuje nám dať externému modulu (kratší) alias, cez ktorý potom k nemu budeme pristupovať. V Elixirovom kóde je použitie aliasov veľmi bežné:

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

# S použitím aliasu

defmodule Example do
  alias Sayings.Greetings

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

# Bez použitia aliasu

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

Ak existuje konflikt medzi dvoma aliasmi alebo iba chceme aliasy pomenovať úplne inak, môžeme tak urobiť pomocou :as:

defmodule Example do
  alias Sayings.Greetings, as: Hi

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

Je možné aliasovať viacero modulov naraz:

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

import

Ak chceme z externého modulu len importovať jeho funkcie a makrá do nášho modulu, môžeme použiť príkaz import:

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

Filtrovanie

Normálne sa pri importe do nášho modulu dostanú úplne všetky funkcie a makrá z modulu, ktorý importujeme. Môžeme však použiť filtre :only (iba) a :except (okrem), ktorými vieme presnejšie špecifikovať, o čo z cieľového modulu máme záujem.

Pri použití filtrov only a except musíme uviesť názov a aritu (počet argumentov) každej filtrovanej funkcie (alebo makra). Napríklad funkciu last/1 by sme samostatne importovali takto:

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

Ak zasa naimportujeme všetko okrem funkcie last/1 a teraz vyskúšame rovnaké funkcie ako predtým dostaneme:

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

Ďalšími užitočnými filtrami sú :functions a :macros, ktorými vieme naimportovať z cieľového modulu len funkcie alebo len makrá:

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

require

Aj keď sa require/2 používa zriedkavejšie, je rovnako dôležitou metódou kompozície. Pri jej použití máme istotu, že cieľový modul je skompilovaný a načítaný. To je užitočné najmä v prípade, že potrebujeme prístup k jeho makrám:

defmodule Example do
  require SuperMacros

  SuperMacros.do_stuff
end

Ak by sme sa totiž pokúsili zavolať makro, ktoré ešte nie je načítané, Elixir by vyhodil chybu.

use

Macro use vyvolá špeciálne macro, nazvané __using__/1, z špecifikovaného modulu. Tu je príklad:

# lib/use_import_require/use_me.ex
defmodule UseImportRequire.UseMe do
  defmacro __using__(_) do
    quote do
      def use_test do
        IO.puts "use_test"
      end
    end
  end
end

a pridáme tento riadok do UseImportRequire:

use UseImportRequire.UseMe

Použitím UseImportRequire.UseMe definuje funkciu use_test/0 tým že vyvolá macro __using__/1.

To je všetko čo use spraví. Ale, je bežné pre macro __using__ použiť ho na zavolanie alias, require alebo import. To vytvorí v module zadané aliasy alebo importy. Umožní nám modul použiť na definovanie politiky, ako máme odkazovať na funkcie a makrá.

Phoenix framework využíva use a __using__/1 na odstránenie opakovania sa pri aliasoch a importoch v moduloch definovaných používateľom.

Tu je krátka ukážka z modulu Ecto.Migration:

defmacro __using__(_) do
  quote location: :keep do
    import Ecto.Migration
    @disable_ddl_transaction false
    @before_compile Ecto.Migration
  end
end

Macro Ecto.Migration.__using__/1 obsahuje volanie import a keď zavoláme use Ecto.Migration tiež zavoláme aj import Ecto.Migration. To pripraví aj atribúty modulu, ktorý ovláda správanie Ecta.

Na zopakovanie: použitie macra jednoducho zavolá __using__/1 špecifikovaného modulu. Aby sme ale naozaj vedeli čo vykoná musíme si prečítať macro __using__/1.

Poznámka: quote, alias, use a require sú makrá použité keď pracujeme s metaprogramovaním.


Contributors

loading...



Zdieľaj túto stránku