Difference Of Alias, Require, Use Keyword In Elixir

출처
Programming Elixir
alias
alias
는 주어진 모듈이름에 대한 별칭을 설정하는 기능이다.- 따라서 특정 모듈에 대한 shorcut을 설정할 때 주로 사용한다.
alias
로 별칭을 설정한 경우에도 Full Path 로 접근이 가능하다.alias
는 선언된 스코프 내에서만 유효하게 작동한다 (lexically scoped)
defmodule Alias do
defmodule Putter do
def puts(var) do
IO.puts var
end
end
end
defmodule LexicalScope do
def scoped do
alias Alias.Putter
Putter.puts "hello world"
end
def no_scoped do
Putter.puts "hello world"
end
end
alias Alias
Alias.Putter.puts("hello")
alias Alias, as: P
P.Putter.puts "hello world!"
alias Alias.Putter, as: PP
PP.puts "hello world!"
LexicalScope.scoped()
# "hello world"
LexicalScope.no_scoped()
# ** (UndefinedFunctionError) function Putter.puts/1 is undefined (module Putter is not available).
Elixir에 정의된 모든 모듈은 Elixir라는 main namespace에 정의되어있다고 할수있다. (ex.
Elixir.String
) 하지만 편의를 위해Elixir
라는 참조 단계는 건너 뛸 수 있다.
require
매크로를 사용하게 하기 위한 키워드이다.
매크로는 매크로를 사용하는 프로그램이 컴파일 되는 시점에, 그 매크로가 어떤 역할을 하는지를 정확하게 알고있어야한다. 이러한 매크로는 어딘가에 정의되어 있을 것이고,
require
는 특정 매크로를 호출한 모듈이 컴파일 되기 전에require
로 호출한 모듈이 컴파일 됨을 보장해준다,- 즉 호출하는 모듈이 컴파일 될 때, 호출받은 매크로가 컴파일되어 호출하는 모듈의 코드를 동적으로 변경시킬 수 있도록 한다.
require
는alias
와 마찬가지로 선언된 스코프 내에서만 유효하다. (lexically scoped)
defmodule RealMacro do
defmacro macro(var) do
IO.inspect var
end
end
defmodule MacroUser do
def run() do
require RealMacro
RealMacro.macro(:some_var)
# :some_var
end
def run_no_req() do
RealMacro.macro(:some_var)
# UndefinedFunctionError) function RealMacro.macro/1 is undefined or private. However, there is a macro with the same name and arity.
end
end
매크로를 사용하는 경우가 아니어도
require
키워드를 사용할 수 있다. 이 경우,require
를 통해 특정 모듈을 호출하는 모듈이 컴파일 되기 전에, 호출받은 모듈이 컴파일 됨을 보장한다.런타임 시점 전에 컴파일되면 성능상 이점이 있는것 아니냐고 오해할 수 있지만, elixir 코드는 어차피 런타임 전에 컴파일 되어 BEAM 바이트 코드로 전환되어 이미 최적화 된 상태라서 의미가 없다.
그럼에도
require
키워드로 사전에 컴파일 하는 이유는, 매크로를 호출하는 모듈이 호출된 매크로가 컴파일 된 이후에 그 내용을 주입받아 동적으로 변경될 수 있어야 하기 때문이다 (위의 설명이랑 동어 반복..)
import
- 일반적으로 다른 모듈의 함수나 매크로를 현재 모듈의 네임스페이스로 직접 가져오는데 사용된다.
iex> import List
List
iex> last([1,2,3,4,5])
5
iex> import List, only: [duplicated: 2]
List
iex> duplicate(:ok, 3)
[:ok, :ok, :ok]
이렇게
import
키워드를 사용하면 List라는 모듈 자체의 네임스페이스를 입력하지 않고도, 그 모듈 내의 함수를 직접 호출 할 수 있다. (매크로도 동일하다.)only
키워드로 특정 함수만import
할 수 있다.except
키워드로 특정 함수를 제외하고import
할 수 있다.require
의 기능을 가지고있어서, 매크로를 불러올 때도 사용할 수 있다. 이때는require
의 기능을 하지만, 해당 매크로의 네임스페이스를 굳이 명시 하지 않아도 된다.
use
복잡하다..
use
의 동작은require
의 기능을 포함한다.use
키워드는 호출되는 모듈의__using__/1
매크로를 호출하여, 그 모듈에서 제공하는 특정 동작이나 설정을 현재 모듈에 주입한다. 따라서,use
는require
의 기능을 포함한다고 할 수 있다. 여기에 더 나아가use
는 현재 모듈에 동적으로 코드를 주입할 수 있는 능력까지 가진다.예를들어 아래의 코드는..
defmodule Example do
use Feature, option: :value
end
- 아래의 코드처럼 컴파일 된다.
defmodule Example do
require Feature
Feature.__using__(option: :value)
end
[OTP의 GenServer 구현 코드] 를 보며 살펴보면 좀 더 이해가 쉽다.
우리는 흔히 GenServer를 사용하기 위해 아래와 같이 구현한다.
defmodule OtpExample do
use GenServer
@me OtpExample
def start_link(init_value) do
GenServer.start_link(__MODULE__, init_value, name: @me)
end
def cast() do
GenServer.cast(@me, :inc)
end
##### IMPL #####
def init(init_value) do
{:ok, init_value}
end
def handle_cast(:inc, val) do
IO.inspect(val)
{:noreply, increment(val)}
end
defp increment(val) do
:timer.sleep(1000)
val + 1
end
end
use GenServer
구문으로 "GenServer를 사용하겠다"고 명시한다.이렇게 되면
GenServer
가require
되고,__using__/1
함수가 실행된다.Genserver의
__using__/1
함수는 GenServer behaviour를 사용한다고 명시되어있다.그리고 기본적인
handle_call/3
,handle_cast/2
,handle_info/2
,terminate/2
등, GenServer behaviour의 동작들을 구현해두었다.이때문에, 위의 예시 코드에서 오직
handle_cast/2
만 구현했음에도 불구하고 실행에 문제가 없었던 것이다.다시말해 use GenServer 키워드를 통해, 이 모듈이 GenSever behaviour의 구조를 따를 것이라는 선언 (
@behaviour GenServer
)을 대신할 수 있었고, 또 해당 behaviour의 인터페이스(?)들을 기본적으로 구현헤주는 코드들을 주입한 것이다.
Subscribe to my newsletter
Read articles from ilsan kim directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

ilsan kim
ilsan kim
Football Fan & Software Developer from South Korea. Goals, Victories, Tricolor Flags. TUI Over GUI. VIM Over VSCode SUPPORT YOUR LOCAL. NOT THAT IN TV. Suwon Samsung Bluewings.