Kiểu dữ liệu Maybe

Kiểu dữ liệu Maybe

Tin ngắn hàng ngày dành cho bạn
  • Cảm ơn threads.net của nhà Meta vì nó là nguồn cảm hứng cho mình tạo ra chuyên mục này trên blog. Ban đầu hơi nghi ngờ về việc liệu tạo ra các bài viết ngắn như thế này có thu hút được người dùng, có ai ngày qua ngày quay trở lại đọc không, hay tất cả chỉ như dã tràng xe cát? Như mình đã nói rất nhiều là làm ra một tính năng không khó, nhưng vận hành nó làm sao cho hiệu quả mới là điều cần phải bận tâm.

    Giờ đây thời gian đã chứng minh tất cả. Chuyên mục Bài viết ngắn luôn đứng trong tốp 5 trang có lượt truy cập nhiều nhất trong ngày/tuần/tháng. Điều đó có nghĩa bạn đọc đã có thói quen quay trở lại nhiều hơn. Tại sao mình lại khẳng định như thế? Vì chuyên mục này gần như không hề được SEO trên các công cụ tìm kiếm như Google.

    Lại kể về thời xa xưa một chút. Thời gian đầu mình rất chịu khó đăng bài trên threads.net với hy vọng thu hút được nhiều người theo dõi, để từ đó khéo léo giới thiệu họ trở thành người dùng blog của mình. Nhưng càng về sau càng thấy "đuối" vì thuật toán của Threads ngày càng không phù hợp với định hướng của mình. Hay nói cách khác là nội dung tạo ra không ăn khách.

    Ví dụ các bài viết của mình thường mang khuynh hướng chia sẻ thông tin, tin tức, hoặc kinh nghiệm cá nhân rút ra sau khi học hoặc làm một cái gì đó. Dường như những bài viết như vậy không được đánh giá cao và thường bị chôn vùi chỉ sau hơn... 100 lượt xem. Hmm... Liệu vấn đề có phải là do mình? Biết thế sao không chịu thay đổi nội dung theo hướng phù hợp hơn với nền tảng?

    Mình đã quan sát Threads, các nội dung dễ lan toả nhất là có yếu tố gây tranh cãi hoặc một định kiến về vấn đề gì đó, đôi khi chỉ đơn giản là phát biểu "ngây ngô" một vấn đề gì đó mà họ biết chắc chắn có tương tác. Mà mình thì gần như là không hề thích định hướng người dùng theo nội dung kiểu này. Mọi người có thể bảo mình bảo thủ, mình chấp nhận. Mỗi người có định hướng nội dung và khán giả khác nhau, lựa chọn nằm ở họ.

    Thế là từ đó mình chủ yếu viết trên này. Chỉ thi thoảng có phát hiện hay lắm thì mới lên Threads "khoe". Ở đây hàng ngày vẫn có người vào đọc, dù cho bạn là ai thì mình tin chắc rằng các bạn nhận ra được thông điệp mà mình muốn truyền tải thông qua mỗi bài viết. Ít nhất chúng ta có chung một định hướng về nội dung. Đôi khi điều sợ nhất không phải là viết ra không ai đọc, mà là họ đọc xong rồi lãng quên trong phút chốc. Số lượng là quan trọng, nhưng chất lượng mới là thứ mang chúng ta lại gần nhau hơn.

    Cảm ơn tất cả 🤓

    » Xem thêm
  • Zed chắc là cộng đồng những nhà phát triển chịu khó lắng nghe người dùng nhất quả đất. Mới đây họ thêm tuỳ chọn để tắt tất tần tật tính năng AI có trong Zed. Trong khi nhiều bên khác đang muốn tích hợp sâu hơn và làm nhiều hơn với AI Agent. Quả là một nước đi táo bạo 🤔

    You Can Now Disable All AI Features in Zed

    » Xem thêm
  • Hôm nay mình đã cố gắng đi hẳn 8k bước trong một phiên để đo lường cho các bạn thấy. Quả là không ngoài dự đoán khi thời gian đi lên đến hơn 1 giờ và quãng đường ~6km 🤓

    À vài hôm nữa là hết tháng, tức là cũng tròn 1 tháng mình bắt đầu thói quen đi bộ mỗi ngày với mục tiêu 8k bước. Để đầu tháng sau mình tổng kết lại xem thế nào luôn ha.

    » Xem thêm

Vấn đề

Promise trong JavaScript chắc hẳn không còn xa lạ với bất kỳ ai. Một Promise đại diện cho một "lời hứa" rằng khi gọi hàm, nó chắc chắn sẽ trả về giá trị trong tương lai. Cho dù là xử lý được (resolve) hay không (reject), thì chúng ta đều đoán được khả năng xảy ra 1 trong 2. Và chắc chắn một điều rằng cả 2 không thể xảy ra cùng một lúc.

database.query('SELECT * ...');
  .then(...);
  .catch(...);

Nếu nghiên cứu kỹ hơn về Promise, bạn sẽ thấy đây là một kiểu kiến trúc rất hay và đặc biệt. Bởi vì nó không giống với các kiểu dữ liệu nguyên thuỷ nào cả, và có một vài tính chất đặc biệt như một cấu trúc dữ liệu, thừa hưởng một số phương thức mà chỉ nó mới có như thencatch. Hơn nữa, nếu thấy một Promise, bạn gần như chắc chắn nên xử lý nó như thế nào.

Ngoài Promise ra, trong nhiều ngôn ngữ lập trình khác nói chung hay JavaScript nói riêng, thì người ta luôn cố gắng tạo ra những cấu trúc dữ liệu an toàn hơn cho lập trình. Trong đó có thể kể đến kiểu Maybe. Vậy thì kiểu dữ liệu "Có lẽ" này có gì đặc biệt, nó giúp ích gì trong lập trình? Câu trả lời có trong bài viết dưới đây.

Kiểu dữ liệu Maybe

Maybe là một cấu trúc dữ liệu đại diện cho việc có hoặc không có giá trị. Ví dụ dưới đây là một triển khai đơn giản nhất cho Maybe:

class Maybe {
  constructor(value) {
    this.value = value;
  }

  isNothing() {
    return this.value === null || this.value === undefined;
  }

  getOrElse(defaultValue) {
    return this.isNothing() ? defaultValue : this.value;
  }
}

Khi đó tạo ra một cấu trúc bằng cách:

const name = new Maybe('2coffee');
console.log(name); // Maybe {value: '2coffee'}
// hay
const unname = new Maybe(); // Maybe {value: undefined}

Hãy lần lượt đi qua các phương thức trong Maybe.

Đầu tiên chúng ta thấy có một isNothing. Phương thức này kiểm tra xem liệu một Maybe là có giá trị hay không có giá trị, nó thường được dùng để xem có giá trị hay không, từ đó xử lý cho phù hợp.

getOrElse trả về giá trị thực của Maybe hoặc một giá trị mặc định được truyền vào nếu Maybe đó đang không có giá trị nào.

Cách dùng cũng đơn giản. Ví dụ, muốn lấy ra giá trị của unname, nếu không có thì trả về 2coffee.

const unname = new Maybe();
const realName = unname.getOrElse('2coffee'); // 2coffee

name là một thực thể của Maybe. Chúng ta biết name chứa một chuỗi 2coffee nhưng không thể áp dụng một số logic thông thường như đối với chuỗi. Ví dụ:

const name = new Maybe('2coffee');
const fullName = name + `.dev`; // [object Object].dev

Đó là vì name giờ đây là một triển khai của Maybe, nó không phải là chuỗi nên không thể thực thi logic như một chuỗi. Thay vào đó chúng ta nên qua một bước lấy giá trị trong Maybe ra trước.

const name = new Maybe('2coffee');
const fullName = name.getOrElse() + '.dev'; // 2coffee.dev

Cách dùng này không an toàn, bởi vì giả sử name không chứa giá trị thì sẽ nhận được kết quả là undefined.dev. Nên cần phải xử lý thêm ngoại lệ cho trường hợp name không có giá trị. Hoặc.

Các kiểu dữ liệu Maybe thường được triển khai thêm phương thức map. Nó cho phép áp dụng một hàm vào giá trị bên trong của Maybe mà không cần phải lấy ra giá trị thực.

class Maybe {
  ...
  static of(value) {
    return new Maybe(value);
  }

  map(fn) {
    if (this.isNothing()) {
      return this;
    }
    return Maybe.of(fn(this.value));
  }
}

map nhận vào một hàm để áp dụng hàm đó vào giá trị bên trong Maybe. static of để gói lại giá trị vào Maybe và trả về.

const name = new Maybe('2coffee');
const makeFullName = (name) => name + '.dev'

const fullName = name.map(makeFullName); // Maybe {value: '2coffee.dev'}

Hơi khó hiểu nhỉ? thế tại sao không khai báo luôn name = '2coffee' đi mà còn phải đi qua Maybe? Bời vì lý do đằng sau cho việc sử dụng Maybe mang lại một số lợi ích nhất định trong các trường hợp nhất định.

Lợi ích trong thực tiễn

Việc áp dụng Maybe trong lập trình giúp chúng ta giảm thiểu được lỗi nullundefined. Bởi vì khi gặp giá trị Maybe, chúng ta bắt buộc phải xử lý dữ liệu theo kiểu Maybe.

const user = null;
const name = user.name; // TypeError: Cannot read properties of null

// so với

const user = Maybe.of(null);
const name = user.map((u) => u.name); // Maybe {value: null}

Một điều mình rất thích và luôn muốn viết chương trình theo kiểu chuỗi xử lý (chaining). Maybe hoàn toàn có thể đáp ứng mà không lo "break" khi gặp giá trị null hoặc undefined.

const data = {
  user: {
    address: {
      city: "Hanoi"
    }
  }
};

const city = Maybe.of(data)
  .map((d) => d.user)
  .map((user) => user.profile)
  .map((address) => address.city);

Ở ví dụ trên, ở map thứ 2, rõ ràng user.profile trả về undefined nhưng map thứ 3 vẫn có thể thực thi mà không gây ra lỗi "Cannot read properties of undefined", vì map đã xử lý ngoại lệ này.

Tương tự, Maybe buộc chúng ta phải xử lý trường hợp không có giá trị. Hãy tưởng tượng Maybe giống như Promise. Một khi đã gọi thì luôn luôn trả về 1 trong hai giá trị là có giá trị và không có giá trị, trong mỗi trường hợp thì phải xử lý sao cho đúng cách để chương trình không gây lỗi trong quá trình chạy.

function findUserById(id) {
  const user = database.find((u) => u.id === id);
  return Maybe.of(user); // Trả về Maybe thay vì null
}

const userName = findUserById(1)
  .map((user) => user.name)
  .getOrElse("User not found");

Ngoài ra, nếu áp dụng tốt Maybe thì chương trình còn trở nên gọn gàng, sạch sẽ hơn nữa. Đôi khi sẽ không còn những câu lệnh điều kiện if-else đầy rối rắm.

Tổng kết

Maybe là một cấu trúc dữ liệu đại diện cho việc có hoặc không có giá trị. Áp dụng Maybe trong lập trình giúp chúng ta giảm thiểu được lỗi nullundefined, xử lý được chaining, phải xử lý tất cả trường hợp có hoặc không có dữ liệu trả về... Bên cạnh Maybe, còn có một cấu trúc dữ liệu khác nữa có tên là Either, bổ sung thêm một số tính chất mà Maybe không có. Chúng ta sẽ cùng nhau tìm hiểu trong bài viết tiếp theo nhé!

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...