---
# System prepended metadata

title: Applicative Functor — Kết hợp song song những “hộp” trong lập trình hàm
tags: [Monoid, Functor, Applicative]

---

# 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

![ChatGPT Image Oct 5, 2025, 10_46_56 AM](https://hackmd.io/_uploads/B1li5PyTxl.png)


> 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. 😎
