Các hàm tạo có hợp lệ trong lập trình hàm không?


17

Các câu hỏi là:

  • Do máy phát điện phá vỡ mô hình lập trình chức năng? Tại sao hay tại sao không?
  • Nếu có, máy phát điện có thể được sử dụng trong lập trình chức năng không và bằng cách nào?

Hãy xem xét những điều sau đây:

function * downCounter(maxValue) {
  yield maxValue;
  yield * downCounter(maxValue > 0 ? maxValue - 1 : 0);
}

let counter = downCounter(26);
counter.next().value; // 26
counter.next().value; // 25
// ...etc

Các downCounterphương pháp có vẻ không quốc tịch. Đồng thời, gọi downCountervới cùng một đầu vào, sẽ luôn dẫn đến cùng một đầu ra. Tuy nhiên, đồng thời, việc gọi điện next()không mang lại kết quả nhất quán.

Tôi không chắc liệu các trình tạo có phá vỡ mô hình lập trình chức năng hay không bởi vì trong ví dụ counternày là một đối tượng trình tạo và do đó việc gọi next()sẽ tạo ra kết quả giống như một đối tượng trình tạo khác được tạo với cùng một chính xác maxValue.

Đồng thời, việc gọi someCollection[3]một mảng sẽ luôn trả về phần tử thứ tư. Tương tự, gọi next()bốn lần trên một đối tượng trình tạo cũng sẽ luôn trả về phần tử thứ tư.

Đối với nhiều bối cảnh hơn, những câu hỏi này đã được đặt ra trong khi làm việc trên một kata lập trình . Người trả lời câu hỏi, đưa ra câu hỏi liệu máy phát điện có thể được sử dụng trong lập trình chức năng hay không và liệu chúng có giữ trạng thái hay không.


2
Mỗi chương trình giữ nhà nước. Câu hỏi thực sự là liệu nó có đủ điều kiện là trạng thái chức năng hay không , mà tôi hiểu là "trạng thái bất biến", trạng thái không thay đổi một khi được gán. Tôi khẳng định rằng cách duy nhất bạn có thể làm cho một trình tạo trả lại một cái gì đó khác nhau trên mỗi cuộc gọi là nếu trạng thái có thể thay đổi có liên quan.
Robert Harvey

Câu trả lời:


14

Các chức năng của máy phát điện không đặc biệt. Chúng ta có thể tự thực hiện một cơ chế tương tự bằng cách viết lại hàm tạo theo kiểu dựa trên cuộc gọi lại:

function downCounter(maxValue) {
  return {
    "value": maxValue,
    "next": function () {
      return downCounter(maxValue > 0 ? maxValue - 1 : 0);
     },
  };
}

let counter = downCounter(26);
counter.value; //=> 26
counter.next().value; //=> 25

Rõ ràng, downCounterlà tinh khiết và chức năng như nó được. Không có vấn đề ở đây.

Giao thức trình tạo được sử dụng bởi JavaScript liên quan đến một đối tượng có thể thay đổi. Điều này là không cần thiết, xem mã trên. Cụ thể, các đối tượng có thể thay đổi có nghĩa là chúng ta mất đi tính minh bạch tham chiếu - khả năng thay thế một biểu thức bằng giá trị của nó. Mặc dù trong ví dụ của tôi, counter.next().valuesẽ luôn luôn đánh giá xem 25nó xảy ra ở đâu và tần suất chúng ta lặp lại, nhưng đây không phải là trường hợp với trình tạo JS - 26sau đó 25, tại một thời điểm , nó thực sự có thể là bất kỳ số nào. Đây là vấn đề nếu chúng ta chuyển tham chiếu đến trình tạo sang chức năng khác:

counter.next().value; //=> 25
otherFunction(counter); // does this consume the counter?
counter.next().value; // what will this be? It depends on the otherFunction()

Vì vậy, rõ ràng, các máy phát giữ trạng thái và do đó không phù hợp với lập trình chức năng tinh khiết của Cameron. May mắn thay, bạn không phải làm lập trình chức năng thuần túy, và thay vào đó có thể thực dụng. Nếu máy phát điện làm cho mã của bạn rõ ràng hơn, bạn nên sử dụng chúng mà không có lương tâm xấu. Xét cho cùng, JavaScript không phải là ngôn ngữ chức năng thuần túy, không giống như Haskell.

Nhân tiện, trong Haskell không có sự khác biệt giữa trả về danh sách và trình tạo, vì nó sử dụng đánh giá lười biếng:

downCounter :: Int -> [Int]
downCounter maxValue =
  maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.