# Applicative Functor — Kết hợp song song những “hộp” trong lập trình hàm
> 📘 *Bài viết thuộc chuỗi “Lập trình hàm qua Functor – Applicative – Monad”, dựa trên ví dụ thực tế trong OCaml.*
---
## 1. Từ Functor đến Applicative
Trước khi nói đến Applicative, ta nhớ lại **Functor**:
Functor cho phép “nâng” một hàm **1 tham số** vào trong ngữ cảnh (hộp).
```ocaml
Option.map (fun x -> x + 1) (Some 3) (* -> Some 4 *)
```
Nhưng nếu hàm có **2 tham số** (`+ : int -> int -> int`) thì sao?
```ocaml
Option.map (+) (Some 3)
(* -> Some (fun y -> 3 + y) *)
```
Kết quả lại là *một hộp chứa một hàm chưa đủ đối số*.
Làm sao cho hàm trong hộp “ăn” thêm đối số khác cũng nằm trong hộp?
Đó chính là lý do **Applicative Functor** ra đời.
---
## 2. Định nghĩa Applicative
Applicative mở rộng từ Functor bằng 2 phép toán mới:
```ocaml
val pure : 'a -> 'a t
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
```
- `pure` đưa một giá trị thường vào hộp (ngữ cảnh).
- `<*>` cho phép áp dụng **hàm trong hộp** lên **giá trị trong hộp**.
Ví dụ:
```ocaml
pure (fun x y -> x + y) <*> Some 3 <*> Some 4
(* -> Some 7 *)
```
Nếu một trong hai hộp là `None` → kết quả cũng `None`.
---
## 3. Ứng dụng thực tế: validate form
Giả sử bạn có form đăng ký với ba trường:
- `name` (không được rỗng)
- `email` (phải chứa @)
- `age` (phải ≥ 18)
Mục tiêu: gom tất cả lỗi cùng lúc, chứ không dừng ở lỗi đầu tiên.
### Kiểu dữ liệu Validation
```ocaml
type 'a validation =
| Ok of 'a
| Errors of string list
let pure x = Ok x
let (<*>) f x =
match f, x with
| Ok f, Ok v -> Ok (f v)
| Errors e1, Ok _ -> Errors e1
| Ok _, Errors e2 -> Errors e2
| Errors e1, Errors e2 -> Errors (e1 @ e2)
```
---
### Hàm kiểm tra từng field
```ocaml
let validate_name n =
if n <> "" then Ok n else Errors ["Name required"]
let validate_email e =
if String.contains e '@' then Ok e else Errors ["Invalid email"]
let validate_age a =
if a >= 18 then Ok a else Errors ["Too young"]
```
---
### Gom kết quả bằng Applicative
```ocaml
type user = { name: string; email: string; age: int }
let make_user name email age = { name; email; age }
let create_user name email age =
pure make_user
<*> validate_name name
<*> validate_email email
<*> validate_age age
```
Khi chạy:
```ocaml
match create_user "" "abc" 15 with
| Ok u ->
Printf.printf "Created: %s, %s, %d\n" u.name u.email u.age
| Errors errs ->
List.iter print_endline errs
```
Kết quả:
```
Name required
Invalid email
Too young
```
---
## 4. Minh họa trực quan

> Mỗi `validate_*` là một hộp, `<*>` kết hợp chúng song song, và cuối cùng `pure make_user` hợp nhất kết quả.
---
## 5. So sánh với Functor và Monad
| Mức độ | Toán tử chính | Ý nghĩa | Khi dùng |
|--------|----------------|----------|-----------|
| **Functor** | `map` | Nâng hàm 1 tham số lên “hộp” | Khi chỉ cần biến đổi 1 giá trị |
| **Applicative** | `pure`, `<*>` | Nâng hàm nhiều tham số, kết hợp song song nhiều “hộp” | Khi có nhiều tác vụ độc lập, cần gom kết quả |
| **Monad** | `>>=` | Chuỗi hóa các bước, bước sau phụ thuộc kết quả bước trước | Khi kết quả bước trước ảnh hưởng đến logic tiếp theo |
Hình dung như ba tầng năng lực:
```
Functor : biến đổi giá trị trong hộp
Applicative : kết hợp song song nhiều hộp
Monad : xâu chuỗi tuần tự nhiều hộp
```
---
## 6. Kết luận
**Applicative Functor** là cầu nối giữa **Functor** và **Monad**:
- Dễ dùng, tổng quát, không cần quan tâm thứ tự.
- Code gọn, tái sử dụng được.
- Đặc biệt hữu ích trong validation, xử lý song song, hoặc lập trình phản ứng (reactive programming).
> Khi bạn thấy code pattern match quá nhiều, hoặc muốn kết hợp nhiều tác vụ độc lập — đó là lúc Applicative lên tiếng. 😎