12/08/2018, 13:40

Tìm hiểu về Imperative và Declarative Programming

Nội dung Giới thiệu Declarative Programming là gì? Lợi ích mang lại của Declarative Programming Kết Luận Giới thiệu Chắc hẳn đa số các bạn lập trình viên đều nghe qua hoặc đã làm quen với khái niệm Functional Programming vì chính bản thân cái tên cũng đã nói lên phần nào đặc trưng ...

Nội dung

  • Giới thiệu
  • Declarative Programming là gì?
  • Lợi ích mang lại của Declarative Programming
  • Kết Luận

Giới thiệu

1425999742-1425306960-paradigm-cover-780x390.jpg

Chắc hẳn đa số các bạn lập trình viên đều nghe qua hoặc đã làm quen với khái niệm Functional Programming vì chính bản thân cái tên cũng đã nói lên phần nào đặc trưng của nó. Nhưng khi google thì mình thấy rằng Functional Programming là một phần của Declarative Programming(DP), thứ mà nằm ở một “thái cực” đối với Imperative Programming(IP) thứ thường được gắn liền với Object-Oriented Programming (OOP) hay gọi là lập trình hướng đối tượng đi cho nó quen thuộc. Hồi còn sinh viên, mình có cảm giác các môn học và chương trình học luôn hướng sinh viên đi theo hướng này. Cũng đúng thôi, Imperative Programming đã quá quen thuộc với lập trình viên nói chung, có thể nói là thống trị thế giới lập trình trong suốt hơn 50 năm qua. Vậy Declarative Programming là gì?

Nào chúng ta cùng tìm hiểu nhé!

Declarative Programming là gì?

Hai mô hình trên vốn khá rộng, nhưng để định nghĩa thì có thể gói gọn 1 cách tương phản rõ ràng như sau:

Imperative Programming: telling the “machine” how to do something, and as a result what you want to happen will happen. Declarative Programming: telling the “machine” what you would like to happen, and let the computer figure out how to do it.

Bạn có thể hình dung là với Imperative Programming thì bạn quan tâm tới việc làm thế nào để giải quyết bài toán còn Declarative Programming quan tâm tới đầu vào và đầu ra của bài toán.

Nghe có vẻ vẫn khá khó hiểu?

7ac3bddd67684ad7992e0f02e02f4f0cbfdbc809513e7049b69483d4d3c358fa.jpg

Bỏ đi, chúng ta cùng xem đoạn code xử lý data viêt theo Imperative Programming sau:

getRemoteData("example.com", { data, error in
  if error == nil {
    parseData(data, { parsed, error in
      if error == nil {
        handleParsedData(parsed, { error in
          if error != nil {
            displayError(error)
          }
        })
      } else {
        displayError(error)
      }
    })
  } else {
    displayError(error)
  }
}

Đoạn code trên nhìn có vẻ rất phức tạp, nó chỉ thực hiện download, parse và handles một vài dữ liệu. Vậy phương án thay thế là như thế nào? Chúng ta thử dùng Declarative programming xem thế nào nhé!

getRemoteData("example.com")
  .then(parseData)
  .then(handleParsedData)
  .onError(displayError)

Bạn có thể thấy rằng các bước thực hiện đã rõ ràng hơn, người khác nhìn vào cũng dễ nhận viết được chức năng cũng như là cách thức thực hiện.

Một ví dụ khác:

Đây là đoạn code xử lý viết theo Imperative Programming:

let array = [1, 2, 3, 4, 5, 6]
var reduced = 0
var filtered = []

for element in array {
  reduced += element
  if element % 2 == 1 {
    filtered.append(element)
  }
}

Còn đây là Declarative Programming:

let numbers = [1, 2, 3, 4, 5, 6]
let sum = reduce(numbers, 0, +)
let odds = filter(numbers, { $0 % 2 == 1})

Rõ ràng là việc viết code trở lên mạch lạc, rõ ràng hơn rất nhiều.

Lợi ích mang lại của Declarative Programming

1. Hạn chế sự thay đổi

Các đối tượng, dữ liệu trong chương trình sẽ rất ít khi bị thay đổi, xuyên suốt trong quá trình thực hiện. Bạn sẽ ít phải bận tâm hơn khi dữ liệu có bị thay đổi ở những hàm nào, luồng (thread) nào tác động đế nó…? Làm việc với các giá trị bất biến (constant) sẽ dễ dàng, ít lỗi hơn rất nhiều. Đó là lý do tại sao Apple khuyến khích dùng “let” trong Swift.

2. Giảm thiểu state side-effects

Đến nay vẫn chưa có định nghĩa cụ thể side-effects là gì? Nhưng hãy tưởng tượng bạn debug 1 lỗi được tạo khi sử dụng function của người động nghiệp viết ra. Bạn tìm ra nguyên nhân là do biến x. Bạn thấy x được gọi trong 10 hàm khác nhau. Và bạn phải lặn ngụp trong 10 hàm đấy để tìm ra nguyên nhân cuối cùng… Boom!

1425999735-1425293914-windows8-bsod-780x488.jpg

Declarative không khuyến khích sử dụng các biến để lưu giữ trạng thái (state), ouput tạm thời của chương trình, thay vào đó các kỹ thuật pipelines, Higher-order function được sử dụng(Google để biết thêm chi tiết). Điều đó hạn chế được các sự thay đổi ngoài ý muốn của state.

Và nếu bạn lập trình đa luồng(multi-thread) hoặc hơn nữa là lập trình tính toán song song sử dụng Multi-core(CPU) thì sẽ thấy việc kiểm soát sự thay đổi data giữa các luồng/CPU core sẽ rất nhức đầu.

3. Code ngắn hơn, dễ hiểu hơn

Ở 2 ví dụ trên tôi đã cho các bạn thấy khi sử dụng Declarative, code của bạn sẽ ngắn hơn và dễ đọc hơn rất nhiều. Nó tập trung vào input, bạn muốn làm gì với input để tạo ra ouput. Hãy hình dung bạn phải đọc hàng loạt các vòng lặp lồng nhau rồi vắt óc xâu chuỗi để biết người tiền nhiệm muốn gì khi viết code này.

4. Dễ dàng mở rộng

Do DP vốn dễ đọc hơn, đơn giản và an toàn hơn nên nó sẽ dễ dàng để mở rộng, thực hiện việc maintain hơn.

Kết Luận

Việc “tẩy não” khi sử dụng Functional Programming khá khó khăn trong những ngày đầu khi tư duy hướng đối tượng đã được định hướng khi mới bắt đầu tiếp xúc với lập trình, gần như ta phải học lại từ đầu (với cá nhân mình thấy là như thế). Tư duy lập trình theo lỗi mòn “how” đã ăn sâu vào máu nên việc chuyển qua tư duy “what” khiến ta gặp nhiều trở ngại. Vậy “how” và “what”, có phải đôi khi ta không cần phải biết “how” mà chỉ cần biết đến mức “what” là đủ ?

Nguồn:

https://www.netguru.co/blog/imperative-vs-declarative

0