Giới thiệu: Bối cảnh Dự án
Trong giai đoạn lập kế hoạch ban đầu cho một dự án quan trọng—một trong những hệ thống mà “nếu nó gặp sự cố, công ty sẽ gặp rắc rối”. Áp lực gia tăng, thời hạn đang đến gần, và ngay cả trước khi viết một dòng mã nào, chúng tôi đã cảm thấy kiến trúc của dự án dường như bị giữ lại bằng băng keo và những lời cầu nguyện tuyệt vọng.
Vào lúc đó, một lập trình viên cao cấp mới, Kazem Arjmand—người dường như rất bình tĩnh trong các cuộc họp thiết kế. Trong khi chúng tôi lo lắng về các lỗi có thể xảy ra trước khi bắt đầu lập trình, anh ấy liên tục nhắc đến một khái niệm gọi là lập trình hàm (FP).
Lúc đó, tôi chỉ biết rằng đó là một phong cách lập trình “học thuật”. Những gì tôi không nhận ra là đây là một triết lý—giống như lập trình hướng đối tượng (OOP)—nhưng được xây dựng dựa trên một vài nguyên tắc cốt lõi:
- Hàm thuần khiết: Hàm luôn trả về kết quả giống nhau với cùng một đầu vào, không có tác dụng phụ.
- Không thay đổi: Dữ liệu không bị thay đổi tại chỗ; thay vào đó, các phiên bản mới được tạo ra.
- Hàm cấp một: Hàm có thể được đối xử như giá trị—được truyền như biến.
- Currying: Biến đổi một hàm với nhiều đối số thành một chuỗi các hàm với một đối số.
Lưu ý: Đây không phải là tất cả về FP—đó chỉ là những chìa khóa cơ bản nhưng quan trọng nhất. Phần còn lại tùy thuộc vào bạn để khám phá.
Tôi nhớ rằng mình đã nghĩ: Thật sao? Giờ không phải là lúc để tái phát minh bánh xe. Chúng ta không cần triết lý mới, chúng ta cần ít cơn đau đầu hơn sắp tới.
Vấn đề: Điều Gì Không Hoạt Động
Kế hoạch dự án của chúng tôi không nhỏ—nó dự kiến có hàng ngàn dòng logic kinh doanh, nhiều dịch vụ truyền trạng thái xung quanh, và nhiều tác dụng phụ hơn cả một viên thuốc giảm cân kém chất lượng. Ngay cả trong giai đoạn thiết kế, chúng tôi có thể dự đoán các lỗi trạng thái sẽ ám ảnh chúng tôi: các giá trị thay đổi một cách không thể đoán trước, các hàm thay đổi đối tượng sau lưng chúng tôi, các điều kiện đua kỳ lạ có khả năng chờ đợi trong sản xuất.
Tôi nhớ những đêm muộn vẽ sơ đồ luồng, lẩm bẩm:
“Nhưng nếu giá trị này đúng ở đây và sai sau đó… làm thế nào chúng ta đảm bảo tính nhất quán?”
Một buổi tối, tôi chia sẻ nỗi lo lắng của mình với Kazem:
Tôi: “Cả cái này sẽ ăn sống tôi. Những lỗi này sẽ giống như đuổi theo ma.”
Kazem: “Bạn nên thử sử dụng hàm thuần khiết và không thay đổi. Nó sẽ giúp mã của bạn có tính dự đoán.”
Trong OOP, chúng tôi thường thay đổi trạng thái đối tượng khi chương trình chạy. Trong FP, bạn làm việc khác đi—bạn viết các hàm biến đổi dữ liệu thay vì thay đổi nó. Đôi khi, bạn thậm chí có thể phân chia các hàm lớn thành các chuỗi nhỏ hơn bằng cách sử dụng currying.
Tính dự đoán nghe như một giấc mơ. Nhưng thật lòng mà nói, tôi nghĩ: Vâng, chắc vậy… và rồng cũng có thật.
Làn Sóng: Khám Phá Lập Trình Hàm
Điểm chuyển biến xảy ra khi lập trình viên cao cấp ngồi xuống với tôi và phác thảo một ví dụ đơn giản về currying:
javascript
// Hàm bình thường
function add(a, b) {
return a + b;
}
add(2, 3); // 5
// Phiên bản curried
function curriedAdd(a) {
return function(b) {
return a + b;
};
}
curriedAdd(2)(3); // 5
// Ví dụ thực tế
const distances = [1,2,6,2]
const factor = getFactor();
const convertMeterToKiloMeter = curriedAdd(factor);
const newDistances = distances.map(convertMeterToKiloMeter);
// [100,200,600,200]
Đột nhiên, mọi thứ trở nên rõ ràng. Đây không phải là lý thuyết trừu tượng—đây là một cách để xây dựng các công cụ có thể tái sử dụng, có thể ghép lại. Các hàm có thể được “tiền nạp” với ngữ cảnh và được tái sử dụng mà không cần viết lại logic.
Tôi (trong đầu): Chờ đã… đây có phải là cảm giác đúng của lập trình không?
Kazem: “Thấy chưa? Các hàm nhỏ có thể được xâu chuỗi và tái sử dụng ở khắp mọi nơi. Nó giống như Lego cho mã.”
Tôi: Cảm xúc. Chính thức. Chấn động.
Cuộc Chiến: Đường Cong Học Tập và Sự Kháng Cự
Tất nhiên, lý thuyết thì đơn giản. Thực hành? Không hề dễ dàng.
Lập trình hàm có bộ công cụ riêng của nó—map
, reduce
, filter
, các hàm bậc cao, currying—và việc sử dụng hàm như các khối xây dựng có thể cảm thấy xa lạ ban đầu.
Việc tái cấu trúc ngay cả các cấu trúc mã đã lập kế hoạch cũng cảm thấy đau đớn. Tôi đã phác thảo một cái gì đó tinh tế, sau đó nhìn chằm chằm vào một vấn đề trong nửa giờ vì tôi quên rằng không thay đổi cấm những thay đổi trực tiếp.
Nhưng những chiến thắng nhỏ đã giúp tôi tiến về phía trước. Như hàm tiện ích thuần đầu tiên mà tôi đã thử nghiệm—không cần mô phỏng, không cần thiết lập đau đầu. Nó chỉ… hoạt động.
Bộ não của tôi đã dao động hàng ngày giữa “Điều này thật tuyệt” và “Tại sao tôi lại ghét bản thân vì đã làm điều này?”
Chiến Thắng: Lợi Ích Thực Sự Quan Sát Được
Khi chúng tôi đi sâu vào việc triển khai, các lợi ích trở nên không thể chối cãi:
- Ít lỗi hơn: mã dự đoán có nghĩa là ít vấn đề “bí ẩn”.
- Dễ dàng kiểm thử: các hàm thuần có thể được kiểm thử riêng biệt.
- Rõ ràng hơn: các hàm trở nên nhỏ hơn và tập trung hơn.
Tôi đã so sánh một trong những bản thiết kế ban đầu của chúng tôi (OOP + không thay đổi) với bản tái viết chức năng mới:
- Trước: tác dụng phụ, thay đổi trạng thái toàn cục, cơn ác mộng gỡ lỗi.
- Sau: hàm thuần, currying cho tính linh hoạt, trạng thái không thay đổi, có thể kiểm thử riêng biệt.
Nó giống như bật đèn trong một căn phòng mà tôi thậm chí chưa bước vào.
Góc Nhìn Rộng Hơn: Lập Trình Hàm Phù Hợp Với OOP Như Thế Nào
Đây là điều: lập trình hàm không nhằm tiêu diệt OOP. Nó bổ sung cho nó.
- Giữ lớp để mô hình hóa miền và đảm bảo cấu trúc.
- Sử dụng FP cho việc xử lý dữ liệu, biến đổi và logic.
Sự kết hợp này đã giúp tôi nhận ra lập trình không phải là về việc chọn một triết lý đúng duy nhất—mà là biết khi nào sử dụng các công cụ phù hợp.
Cách Độc Giả Có Thể Bắt Đầu Hành Trình Của Họ
Nếu bạn tò mò về lập trình hàm nhưng cảm thấy áp lực, hãy bắt đầu từ những điều nhỏ:
- Viết hàm thuần đầu tiên của bạn—không có tác dụng phụ.
- Thay thế một vòng lặp bằng
map
hoặcfilter
. - Thử một hàm curried để xem cảm giác của việc áp dụng một phần.
Hãy nghĩ về nó như bánh xe tập đi—bạn không cần phải lật ngược toàn bộ dự án của mình vào ngày đầu tiên.
Kết luận: Suy Ngẫm và Lời Mời
Dự án đó cuối cùng trở thành một trong những dự án hiếm hoi mà tôi nhìn lại với sự tự hào—không phải sợ hãi. Lập trình hàm không phải là một viên đạn bạc, nhưng nó đã định hình lại tư duy của tôi và cho tôi một bộ công cụ mới để chiến đấu với sự phức tạp.
Nếu bạn từng cảm thấy như mình đang chiến đấu với những con quỷ mã không thể đoán trước, có lẽ đã đến lúc thử hàm thuần—và có thể cả một chút currying—một lần nữa. Tương lai của bạn sẽ cảm ơn bạn.
Và nếu bạn đã thực hiện bước nhảy vào FP, tôi rất muốn nghe câu chuyện của bạn. Ai biết được—bạn có thể truyền cảm hứng cho lập trình viên hoài nghi tiếp theo để nhảy vào.
Khám Phá Thêm
- Scalfani, Charles — "Lập Trình Hàm Trở Nên Dễ Dàng Hơn: Hướng Dẫn Bước-Đi-Bước." Một con đường từ người mới đến nâng cao sử dụng PureScript, bao gồm các yếu tố thiết yếu như hàm thuần, không thay đổi, currying, kết hợp, ADTs, functors, applicatives, monads, và thậm chí là các bài tập thực tiễn toàn diện; lý tưởng sau khi học các kiến thức cơ bản trong JS để làm sâu sắc thêm tư duy FP thực sự.
Lưu ý: đây là cuốn sách mà tôi đã học rất nhiều trong hành trình FP, và đề cập này không phải là quảng cáo—đơn giản chỉ là một lời giới thiệu chân thành.