Phân biệt các từ khóa use, alias, require và import
Tags: elixir
Published: 2021-08-21 07:09:53

Khi mới tiếp cận với `Elixir` các bạn có thể sẽ phân vân điểm khác nhau giữa các từ khoá trên. Bài viết này so sánh các từ khoá đó thông qua cách sử dụng

# 1. import
Hiểu đơn giản nó sẽ bê toàn bộ functions và macros của module B vô module A để sử dụng. Mình có thể gọi tắt 1 function trong B mà không cần bắt đầu bằng tên module. 

```ruby
defmodule A do
  def say_world() do
    IO.puts("world")
  end

  defmacro if_unless(expr, opts) do
    quote do
      if !unquote(expr), unquote(opts)
    end
  end
end

defmodule B do
  import A

  def say_hello do
    IO.puts("Hello ")
    if_unless false do
      say_world()
    end
  end
end
```

Có thể sử dụng thêm 2 từ khóa `only` nếu chỉ muốn lấy 1 số hàm, marco hoặc từ khóa `except` nếu muốn loại từ hàm hay macro nào đó

```ruby
import List, only: [flatten: 1]
import String, except: [split: 2]
```

Import có thể sử dụng bên trong function và có scope chỉ ở trong function đó.

```ruby
defmodule Math do
	def some_function do
		# 1) Disable "if/2" from Kernel
		import Kernel, except: [if: 2]

		# 2) Require the new "if/2" macro from MyMacros
		import MyMacros

		# 3) Use the new macro
		if do_something, it_works
	end
end
```

**Lưu ý**: Các hàm bắt đầu bằng `_` sẽ không được import. Ví dụ: (`__build__`, ...)

# 2. require
Khi muốn sử dụng marco của 1 module nào đó, bắt buộc phải require module đó trước khi gọi marco

```ruby
defmodule A do
  def say_world() do
    IO.puts("world")
  end

  defmacro if_unless(expr, opts) do
    quote do
      if !unquote(expr), unquote(opts)
    end
  end
end

defmodule B do
  require A

  def say_hello do
    IO.puts("Hello ")

    A.if_unless false do
      A.say_world()
    end
  end
end
```

#3. alias
Dùng để setup 1 alias name cho 1 module. Mặc định thì alias sẽ lấy phần cuối cùng trong tên module làm tên alias.

```ruby
defmodule A do
	alias Foo.Bar.Baz

	def funcA do
    	Baz.some_func()
    end
end
```

Có thể sử dụng key `as` để định nghĩa 1 tên khác.

```ruby
defmodule A do
	alias Foo.Bar.Baz, as: FBar

	def funcA do
    	FBar.some_func()
    end
end
```

Mình có thể định nghĩa nhiều alias có module tương tự nhau trong cùng 1 hàng

```ruby
alias Foo.Bar
alias Foo.Baz
alias Foo.Biz

> alias Foo.{Bar, Baz, Biz}
```

**Chú ý:** `import/2`, `require/2` và `alias/2` có thế định nghĩa trong từng function khác nhau không nhất thiết phải để ngay sau  `defmodule` và nó không ảnh hưởng scope lẫn nhau.


#4. use
Thường xài kiểu như abstract, Khi module B use module A, nó sẽ invoke marco `__using__` trong module A

```ruby
defmodule A do
  defmacro __using__(opts) do
    quote do
      IO.puts("Hello ")
    end
  end
end

defmodule B do
  use A, key1: "abc"

  def say_hello do
    IO.puts("World")
  end
end
```

đoạn code trên tương đương với
 
```ruby
defmodule B do
  B.__using__([key1: "abc"])

  def say_hello do
    IO.puts("World")
  end
end
```
 
`use` thường được dùng khi muốn định nghĩa 1 behavior cho 1 module. Ví dụ trong Phoenix Framework. Controller được định nghĩa

```ruby
defmodule ApiWeb do
...
	def controller do
		quote do
			use Phoenix.Controller, namespace: ApiWeb
			import Plug.Conn
			import ApiWeb.Router.Helpers
			import ApiWeb.Gettext
			require Logger
		end
	end
...
end

defmodule ApiWeb.PageController do
	use ApiWeb, :controller
    ...
end
```
**Lưu ý**: Không nên định nghĩa hàm ở trong `__using__/1`


# Kết
Các bạn có thể tham gia vào channel `#elixir` ở  [cộng đồng Ruby Việt Nam](http://chat.ruby.org.vn/) để thảo luận thêm về các vấn đề khi lập trình với `elixir` :)