Skip to content

Commit

Permalink
Translate to vietnamese (elixirschool#918)
Browse files Browse the repository at this point in the history
🎆  A complete site translation to Vietnamese 🇻🇳 

Kudos to **Elixir Vietnam Group developers** 👏 

@kiennt @huydx @huynhquancam @MQuy @namhoai95 @nguyenduyhao1111
  • Loading branch information
kiennt authored and Trung Lê committed Jan 23, 2017
1 parent 71564b6 commit 50c8007
Show file tree
Hide file tree
Showing 32 changed files with 2,641 additions and 595 deletions.
2 changes: 2 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ sections:
bn: নিয়মাবলি
- tag: libraries
label:
vi: Thư viện
en: Libraries
jp: ライブラリー
ru: Библиотеки
Expand Down Expand Up @@ -189,3 +190,4 @@ share:
bg: Споделете тази страница
pt: Compartilhe essa página
bn: পৃষ্ঠা শেয়ার করুন
vi: Chia sẻ trang này
118 changes: 118 additions & 0 deletions vi/lessons/advanced/behaviours.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
layout: page
title: Behaviours
category: advanced
order: 10
lang: vi
---

Chúng ta đã học về Typespecs trong bài học trước, trong bài này, chúng ta sẽ học về cách để yêu cầu một module cài đặt những tiêu chuẩn đó. Trong Elixir, tính năng này thường được gọi bằng Behaviours.

{% include toc.html %}

## Sử dụng

Đôi khi, bạn muốn các module cùng chia sẻ một public API, giải pháp cho vấn đề này trong Elixir là behaviours. Behaviours thực thi hai vai trò chính:

+ Định nghĩa một tập các hàm bắt buộc phải cài đặt
+ Kiểm tra xem tập đó có được cài đặt hay không

Elixir đã bao gồm một tập các behaviours ví dụ như GenServer, nhưng trong bài học này, chúng ta sẽ tập trung vào việc tạo ra các Behaviour cho riêng chúng ta.

## Định nghĩa một behaviour

Để hiểu rõ về behaviour hơn, hãy cùng cài đặt một worker module. Những worker này được kỳ vọng sẽ cài đặt 2 hàm `init/1``perform/2`.

Để đạt được điều đó, chúng ta sẽ sử dụng `@callback` với cú pháp tương tự như `@spec`, để định nghĩa các hàm bắt buộc (__required__ method), đối với macro, chúng ta có thể sử dụng `@macrocallback`. Hãy cùng xác định `init/1``perform/2` cho các worker của chúng ta:

```elixir
defmodule Example.Worker do
@callback init(state :: term) :: {:ok, new_state :: term} | {:error, reason :: term}
@callback perform(args :: term, state :: term) :: {:ok, result :: term, new_state :: term} | {:error, reason :: term, new_state :: term}
end
```

Ở đây chúng ta định nghĩa `init/1` chấp nhận bất cứ giá trị nào, và trả về một tuple hoặc là `{:ok, state}` hoặc là `{:error, reason}`, đây là tiêu chuẩn cho việc khởi tạo. Hàm `perform/2` sẽ nhận vào một vài tham số cho worker cùng với trạng thái mà chúng ta đã khởi tạo, chúng ta kỳ vọng rằng `perform/2` sẽ trả về `{:ok, result, state}` hoặc là `{:error, reason, state}` giống như GenServer.

## Sử dụng behaviours

Giờ đây chúng ta đã định nghĩa behaviour, chúng ta có thể sử dụng nó để tạo ra một số module mà tất cả chia sẻ cùng một public API. Thêm một behaviour vào module khá là dễ với thuộc tính `@behaviour`.

Sử dụng behaviour mới, chúng ta sẽ tạo ra một module để tải một file, sau đó lưu nó lại:

```elixir
defmodule Example.Downloader do
@behaviour Example.Worker

def init(opts), do: {:ok, opts}

def perform(url, opts) do
url
|> HTTPoison.get!
|> Map.fetch(:body)
|> write_file(opts[:path])
|> respond(opts)
end

defp write_file(:error, _), do: {:error, :missing_body}
defp write_file({:ok, contents}, path) do
path
|> Path.expand
|> File.write(contents)
end

defp respond(:ok, opts), do: {:ok, opts[:path], opts}
defp respond({:error, reason}, opts), do: {:error, reason, opts}
end
```

Vậy còn một worker để nén một mảng các file thì sao? Chúng ta cũng có thể làm như sau:

```elixir
defmodule Example.Compressor do
@behaviour Example.Worker

def init(opts), do: {:ok, opts}

def perform(payload, opts) do
payload
|> compress
|> respond(opts)
end

defp compress({name, files}), do: :zip.create(name, files)

defp respond({:ok, path}, opts), do: {:ok, path, opts}
defp respond({:error, reason}, opts), do: {:error, reason, opts}
end
```

Trong khi các công việc được thực hiện là khác nhau, nhưng public API thì không, và bất cứ đoạn code nào sử dụng các module này, để có thể tương tác với những module đó và biết rằng chúng sẽ nhận được kết quả trả về như mong muốn. Điều này cho phép chúng ta khả năng tạo ra rất nhiều loại worker, thực hiện những nhiệm vụ khác nhau, nhưng cùng sử dụng chúng một public API.

Nếu chúng ta muốn thêm vào một behaviour, nhưng lại không cài đặt đủ tất cả các hàm cần thết, một cảnh báo sẽ được văng ra lúc biên dịch. Hãy cùng thay đổi `Example.Compressor` bằng cách bỏ đi hàm `init/1`:

```elixir
defmodule Example.Compressor do
@behaviour Example.Worker

def perform(payload, opts) do
payload
|> compress
|> respond(opts)
end

defp compress({name, files}), do: :zip.create(name, files)

defp respond({:ok, path}, opts), do: {:ok, path, opts}
defp respond({:error, reason}, opts), do: {:error, reason, opts}
end
```

Giờ đây khi biên dịch đoạn code trên, chúng ta có thể thấy cảnh báo:

```shell
lib/example/compressor.ex:1: warning: undefined behaviour function init/1 (for behaviour Example.Worker)
Compiled lib/example/compressor.ex
```

Vậy là hết rồi. Giờ chúng ta đã sẵn sàng để tạo mới và chia sẻ behaviour với các module khác.
42 changes: 25 additions & 17 deletions vi/lessons/advanced/concurrency.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
---
layout: page
title: Concurrency
title: Xử lý đồng thời
category: advanced
order: 4
lang: vi
---

One the selling points of Elixir is its support for concurrency. Thanks to the Erlang VM (BEAM), concurrency in Elixir is easier than expected. The concurrency model relies on Actors, a contained process that communicates with other processes through message passing.
Một trong những điểm nổi bật của Elixir đó là việc hỗ trợ xử lý đồng thời. Nhờ có máy ảo Erlang (BEAM), việc xử lý đồng thời trong Elixir dễ hơn rất nhiều so với mong đợi. Mô hình xử lý đồng thời dựa và Actor, một process có thể tương tác với các process khác thông qua việc truyền thông điệp.

Trong bài học này, chúng ta xem cách các module xử lý đồng thời làm việc trong Elixir. Trong chương kế tiếp chúng ta sẽ học về OTP, và cách cài đặt chúng.

In this lesson we'll look at the concurrency modules that ship with Elixir. In the following chapter we cover the OTP behaviors that implement them.

{% include toc.html %}

## Processes

Processes in the Erlang VM are lightweight and run across all CPUs. While they may seem like native threads they're simpler and it's not uncommon to have thousands of concurrent process in an Elixir application.
Process trong máy ảo Erlang là nhẹ (nhẹ ở đây hiểu theo nghĩa nó là process được cài đặt ở không gian của người dùng, thay vì không gian của nhân hệ điều hành) và chạy trên tất cả các CPU. Trong khi chúng có vẻ như là các native thread, chúng đơn giản hơn nhiều, và khá là bình thường nếu một ứng dụng Elixir có hàng ngàn process chạy cùng nhau.

The easiest way to create a new process is `spawn` which takes either an anonymous or named function. When we create a new process it returns a _Process Identifier_, or PID, to uniquely identify it within our application.
Cách dễ nhất để tạo mới một process đó là `spawn`, hàm này sẽ nhận vào một hàm anonymous hoặc là một hàm có tên. Khi chúng ta tạo mới một process, nó sẽ trả về một _Process Identifier_, hoặc là PID, giá trị này là duy nhất trong ứng dụng của chúng ta.

To start we'll create a module and define a function we'd like to run:
Để bắt đầu, chúng ta sẽ tạo ra một module, và định nghĩa một hàm chúng ta muốn chạy:

```elixir
defmodule Example do
Expand All @@ -32,24 +33,26 @@ iex> Example.add(2, 3)
:ok
```

To evaluate the function asynchronously we use `spawn/3`:
Để chạy hàm này một cách bất đồng bộ, chúng ta sử dung `spawn/3`:

```elixir
iex> spawn(Example, :add, [2, 3])
5
#PID<0.80.0>
```

### Message Passing
### Truyền thông điệp

To communicate, processes rely on message passing. There are two main components to this: `send/2` and `receive`. The `send/2` function allows us to send messages to PIDs. To listen we use `receive` to match messages, if no match is found the execution continues uninterrupted.
Để tương tác với nhau, các process dựa vào cơ chế truyền thông điệp. Có hai thành phần chính để làm chuyện này: `send/2` `receive`. Hàm `send/2` cho phép chúng ta truyền một thông điệp tới PID. Để lắng nghe, chúng ta sử dụng `receive` và so trùng thông điệp. Nếu không có thông điệp vào được so trùng, việc hoạt động của process vẫn được tiến hành mà không bị ngưng lại.

```elixir
defmodule Example do
def listen do
receive do
{:ok, "hello"} -> IO.puts "World"
end

listen
end
end

Expand All @@ -64,9 +67,11 @@ iex> send pid, :ok
:ok
```

### Process Linking
Bạn có thể chú ý rằng hàm `listen/0` là đệ quy, điều này cho phép process của chúng ta có thể xử lý nhiều thông điệp. Nếu không có đệ quy, process sẽ bị thoát ra sau khi xử lý thông điệp đầu tiên.

### Liên kết các process

One problem with `spawn` is knowing when a process crashes. For that we need to link our processes using `spawn_link`. Two linked processes will receive exit notifications from one another:
Một vấn đề của `spawn` đó là cần phải biết khi một process bị crash. Để làm điều này, chúng ta sẽ cần liên kết các process lại với nhau bằng hàm `spawn_link`. Hai process được liên kết với nhau sẽ nhận được thông báo khi process kia bị thoát:

```elixir
defmodule Example do
Expand All @@ -80,7 +85,7 @@ iex> spawn_link(Example, :explode, [])
** (EXIT from #PID<0.57.0>) :kaboom
```

Sometimes we don't want our linked process to crash the current one. For that we need to trap the exits. When trapping exits they will be received as a tuple message: `{:EXIT, from_pid, reason}`.
Đôi khi, chúng ta không muốn process được liên kết làm cho process hiện tại bị crash. Vì thế chúng ta cần đánh bẫy sự thoát ra của process kia. Khi đánh bẫy sự thoát ra, chúng ta sẽ nhận được một thông điệp dạng tuple như sau: `{:EXIT, from_pid, reason}`.

```elixir
defmodule Example do
Expand All @@ -100,9 +105,11 @@ Exit reason: kaboom
:ok
```

### Process Monitoring
### Giám sát process

What if we don't want to link two processes but still be kept informed? For that we can use process monitoring with `spawn_monitor`. When we monitor a process we get a message if the process crashes without our current process crashing or needing to explicitly trap exits.
Vậy nếu chúng ta không muốn liên kết hai process, nhưng vẫn muốn được thông báo? Trong trường hợp này, chúng ta có thể giám sát process bằng hàm `spawn_monitor`. Khi chúng ta giám sát một process, chúng ta sẽ nhận được một thông điệp nếu process bị crash mà không làm process hiện tại bị crash hoặc là cần phải đánh bẫy thoát một cách minh bạch.

Khi giám sát một process, nếu process đó bị crash, process hiện tại sẽ nhận được một thông điệp dạng `{:DOWN, ref, :process, from_pid, reason}`.

```elixir
defmodule Example do
Expand All @@ -123,7 +130,7 @@ Exit reason: kaboom

## Agents

Agents are an abstraction around background processes maintaining state. We can access them from other processes within our application and node. The state of our Agent set to our function's return value:
Agents là một mức trừu tượng hoá lên các process nền để lưu giữ trạng thái. Chúng ta có thể truy cập chúng từ các process khác trong ứng dụng và các node. Trạng thái của một Agent được gán bằng giá trị trả về của hàm:

```elixir
iex> {:ok, agent} = Agent.start_link(fn -> [1, 2, 3] end)
Expand All @@ -136,7 +143,7 @@ iex> Agent.get(agent, &(&1))
[1, 2, 3, 4, 5]
```

When we name an Agent we can refer to it by that instead of it's PID:
Khi chúng ta đặt tên một Agent, chúng ta có thể trỏ tới nó bằng tên thay vì PID:

```elixir
iex> Agent.start_link(fn -> [1, 2, 3] end, name: Numbers)
Expand All @@ -148,7 +155,8 @@ iex> Agent.get(Numbers, &(&1))

## Tasks

Tasks provide a way to execute a function in the background and retrieve its return value later. They can be particularly useful when handling expensive operations without blocking the application execution.
Tasks cung cấp một cách để chạy một hàm dưới nền, và lấy ra giá trị trả về lúc sau. Chúng có thể cực kỳ hữu dụng khi muốn xử lý các hoạt động tốn chi phí mà không làm chậm lại ứng dụng.


```elixir
defmodule Example do
Expand Down
43 changes: 22 additions & 21 deletions vi/lessons/advanced/erlang.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
---
layout: page
title: Tương tác với Erlang
title: Erlang Interoperability
category: advanced
order: 1
lang: vi
---

Một trong những lợi ích có được khi xây dựng dựa trên Erlang VM (BEAM) là sự phong phú của các thư viện có sẵn. Khả năng tương tác với Erlang cho phép ta sử dụng những thư viện này cũng như thư viện chuẩn của Erlang khi viết code bằng Elixir. Ở bài này, chúng ta sẽ xem xét việc truy cập những tính năng của thư viện chuẩn cũng như thư viện bên thứ ba của Erlang.
Một trong những lợi ích của việc xây dựng dựa trên Erlang VM (BEAM) chính việc có rất nhiều những thư viện mà chúng ta thể sử dụng. Tính tương tác này giúp chúng ta có thể tận dụng những thư viện đó cũng như thư viện chuẩn của Erlang từ Elixir code. Trong bài này chúng ta sẽ xem làm thế nào để sử dụng được những tính năng bên trong thư viện chuẩn cũng như thư viện của bên thứ ba từ Erlang.

{% include toc.html %}

## Thư viện chuẩn
Chúng ta có thể sử dụng một cách rộng rãi các thư viện chuẩn của Erlang ở bên trong ứng dụng Elixir. Module Erlang được biểu diễn bằng các atom không viết hoa (lowercase) như là `:os` hay là `:timer`.

Thư viện chuẩn phong phú của Erlang có thể được truy cập từ bất kỳ đoạn code Elixir nào trong hệ thống. Module của Erlang được ký hiệu bởi atom trong chữ in thường như là `:os``:timer`.

Hãy thử dùng `:timer.tc` để tính thời gian chạy của một hàm đã cho:
Hãy thử sử dụng `:timer.tc` để đo thời gian chạy của một hàm:

```elixir
defmodule Example do
Expand All @@ -30,19 +29,19 @@ Time: 8ms
Result: 1000000
```

Để có một danh sách đầy đủ các module sẵn có, xem thêm [Erlang Reference Manual (Sổ tay tra cứu Erlang)](http:https://erlang.org/doc/apps/stdlib/).
Để xem những module nào có thể sử dụng được, hãy xem [Hướng dẫn tham khảo Erlang](http:https://erlang.org/doc/apps/stdlib/).

## Erlang Packages (Thư viện bên thứ ba của Erlang)
## Gói thư viện Erlang

một bài trước, ta đã tìm hiểu về Mix và cách quản lý dependencies (thành phần phụ thuộc), bao gồm các thư viện Erlang cũng có cơ chế hoạt động như vậy. Trong trường hợp thư viện Erlang đó chưa có trên [Hex](https://hex.pm) bạn có thể trỏ tới git repo:
Ở bài trước chúng ta đã học về Mix và quản lý thư viện phụ thuộc. Thêm thư viện Erlang vào cũng tương tự như vậy. Trong trường hợp thư viện Erlang không nằm trên [Hex](https://hex.pm) bạn có thể tham khảo về cách sử dụng git repository như dưới đây:

```elixir
def deps do
[{:png, github: "yuce/png"}]
end
```

Và giờ ta có thể truy cập thư viện Erlang:
Sau đó chúng ta có thể truy cập vào thư viện Erlang:

```elixir
png = :png.create(%{:size => {30, 30},
Expand All @@ -51,13 +50,13 @@ png = :png.create(%{:size => {30, 30},
:palette => palette})
```

## Những điểm khác biệt cần lưu ý
## Những khác biệt đáng chú ý

Sau khi đã biết cách sử dụng Erlang ta nên điểm lại những sai lầm dễ mắc phải khi tương tác với Erlang.
Khi chúng ta đã biết cách sử dụng Erlang, chúng ta cũng nên xem cả những điểm chốt đi cùng với việc tương tác với Erlang.

### Atom
### Atoms

Các atom trong Erlang trông giống như trong Elixir nhưng không có dấu hai chấm (`:`). Chúng được ký hiệu bởi string chữ thường và underscore (đường gạch dưới):
Atoms của Erlang vẻ ngoài nhìn giống như bản sao bên Elixir khi không có dấu hai chấm (`:`). Chúng được biểu diễn bởi chuỗi kí tự không viết hoa và dấu gạch dưới:

Elixir:

Expand All @@ -71,15 +70,17 @@ Erlang:
example.
```

### String (Chuỗi)
### Chuỗi kí tự

Khi nói đến string trong Elixir ta nói đến binaries (chuỗi nhị phân) được mã hoá theo UTF-8. Với Erlang, string vẫn sử dụng double quotes (dấu phẩy kép ") nhưng lại là các char list (danh sách ký tự):
Ở Elixir khi chúng ta nói về chuỗi kí tự chúng ta nói về chuỗi binary được mã hoá dưới dạng UTF-8. Với Erlang, chuỗi kí tự vẫn được biểu diễn bởi dấu ngoặc kép, nhưng thực tế lại được chỉ đến một chuỗi các kí tự đơn (char list):

Elixir:

```elixir
iex> is_list('Example')
true
iex> is_list("Example")
false
iex> is_binary("Example")
true
iex> <<"Example">> === "Example"
Expand All @@ -91,15 +92,15 @@ Erlang:
```erlang
1> is_list('Example').
false
1> is_list("Example").
2> is_list("Example").
true
1> is_binary("Example").
3> is_binary("Example").
false
1> is_binary(<<"Example">>).
4> is_binary(<<"Example">>).
true
```

Điều cần phải chú ý ở đây là nhiều thư viện cũ của Erlang không hỗ trợ binaries (chuỗi nhị phân), vì vậy ta phải biến đổi string Elixir thành char list (danh sách ký tự):
Nên chú ý rằng rất nhiều thư viện Erlang không hỗ trợ chuỗi binary, với trường hợp đó chúng ta cần chuyển đổi chuỗi kí tự Elixir sang các chuỗi kí tự đơn. May mắn là việc đó có thể làm khá dễ dàng với hàm `to_charlist/1`:

```elixir
iex> :string.words("Hello World")
Expand All @@ -112,7 +113,7 @@ iex> "Hello World" |> to_charlist |> :string.words
2
```

### Variables
### Biến

Elixir:

Expand All @@ -134,4 +135,4 @@ Erlang:
11
```

Vậy đó! Sử dụng Erlang từ hệ thống Elixir thật dễ dàng và nhân đôi số lượng thư viện ta có thể sử dụng.
Đơn giản vậy đó! Tận dụng Erlang từ bên trong ứng dụng Elixir vô cùng dễ dàng và qua đó nhân đôi số lượng thư viện mà chúng ta có thể sử dụng được.
Loading

0 comments on commit 50c8007

Please sign in to comment.