2013-05-20 30 views
18

Gần đây tôi đã nghiên cứu sự khác biệt giữa Đề án và Lisp chung về cách tiếp cận mà hai ngôn ngữ này hướng tới việc tiếp tục.Tại sao không tồn tại một nguyên thủy `call-with-current-continuations` trong Common Lisp?

Tôi đã nhận thấy rằng phương pháp tiếp cận chung của Lisp là bảo thủ hơn so với cách tiếp cận của Đề án.

Hơn nữa Đề án cung cấp nguyên thủy call-with-current-continuation, thường viết tắt call/cc, không tương đương với đặc tả ANSI thường gặp của ANSI (mặc dù có một số thư viện cố gắng triển khai chúng).

Có ai biết lý do tại sao quyết định không tạo ra nguyên thủy tương tự trong đặc tả ANSI thường gặp ANSI đã được thực hiện?

Xin cảm ơn trước.

+3

continuations First class hỗ trợ thêm một chi phí thời gian chạy. –

Trả lời

19

Lisp thường có mô hình biên dịch tệp chi tiết như một phần của ngôn ngữ chuẩn. Mô hình hỗ trợ biên dịch chương trình thành các tệp đối tượng trong một môi trường và tải chúng vào một hình ảnh trong một môi trường khác. Không có gì so sánh được trong Đề án. Không eval-when hoặc compile-file, load-time-value hoặc các khái niệm như đối tượng bên ngoài là gì, cách ngữ nghĩa trong mã được biên dịch phải đồng ý với mã được giải thích. Lisp có một cách để có các chức năng được gạch chân hoặc không để chúng được gạch chân, và về cơ bản bạn kiểm soát với độ chính xác cao những gì xảy ra khi một mô-đun được biên dịch được nạp lại.

Ngược lại, cho đến khi sửa đổi gần đây báo cáo Đề án, ngôn ngữ lược đồ hoàn toàn im lặng về chủ đề về cách chương trình Đề án được chia thành nhiều tệp. Không có chức năng hoặc macro nào được cung cấp cho điều này. Nhìn vào R5RS, dưới 6.6.4 System Interface. Tất cả những gì bạn phải có một định nghĩa rất lỏng lẻo load chức năng:

tùy chọn thủ tục: (load filename)

Tên tập tin phải là một chuỗi đặt tên một tập tin hiện có chứa mã nguồn Scheme. Thủ tục tải đọc biểu thức và định nghĩa từ tệp và đánh giá chúng tuần tự. Nó không được chỉ định cho dù kết quả của các biểu thức được in. Quy trình tải không ảnh hưởng đến các giá trị được trả về bởi cổng vào-hiện tại và cổng đầu ra-hiện tại. Tải trả về một giá trị không xác định.

Lý do: Đối với tính di động, tải phải hoạt động trên các tệp nguồn. Hoạt động của nó trên các loại tệp khác nhất thiết phải khác nhau giữa các lần triển khai.

Vì vậy, nếu đó là mức độ tầm nhìn của bạn về cách ứng dụng được xây dựng từ mô-đun và tất cả các chi tiết bên ngoài để người thực hiện làm việc, tất nhiên bầu trời là giới hạn về phát minh ngữ nghĩa ngôn ngữ lập trình. Lưu ý một phần phần Cơ sở lý luận: nếu load được định nghĩa là hoạt động trên tệp nguồn (với tất cả khác là tiền thưởng lịch sự của người triển khai) thì không có gì ngoài cơ chế bao gồm văn bản như #include bằng ngôn ngữ C và do đó ứng dụng Đề án thực sự chỉ là một phần văn bản được chia thành nhiều tệp văn bản được ghép với nhau bởi load.

Nếu bạn đang nghĩ đến việc thêm bất kỳ tính năng nào vào Common Lisp, bạn phải suy nghĩ về cách nó phù hợp với mô hình biên dịch và tải động chi tiết, trong khi vẫn giữ được hiệu suất tốt mà người dùng mong đợi.

Nếu tính năng bạn đang nghĩ đến đòi hỏi tối ưu toàn cầu, toàn bộ chương trình (theo đó hệ thống cần xem mã nguồn cấu trúc của mọi thứ) để chương trình của người dùng không chạy kém (và trong các chương trình cụ thể mà không ' t sử dụng tính năng đó) thì nó sẽ không thực sự bay.

Cụ thể liên quan đến ngữ nghĩa của các lần tiếp tục, có vấn đề. Trong ngữ nghĩa thông thường của một phạm vi khối, một khi chúng ta để lại một phạm vi và thực hiện dọn dẹp, đó là đi; chúng ta không thể quay trở lại phạm vi đó trong thời gian và tiếp tục tính toán. Lisp thường là bình thường theo cách đó. Chúng tôi có cấu trúc unwind-protect thực hiện các hành động dọn dẹp vô điều kiện khi phạm vi kết thúc. Đây là cơ sở cho các tính năng như with-open-file cung cấp đối tượng xử lý tệp mở cho một phạm vi khối và đảm bảo rằng điều này bị đóng bất kể phạm vi chặn kết thúc như thế nào. Nếu tiếp tục thoát khỏi phạm vi đó, việc tiếp tục đó không còn có tệp hợp lệ nữa. Chúng tôi không thể chỉ đơn giản là không đóng tệp khi chúng tôi rời khỏi phạm vi vì không có sự đảm bảo rằng việc tiếp tục sẽ được sử dụng; đó là để nói, chúng ta phải giả định rằng phạm vi là trong thực tế bị bỏ rơi mãi mãi và làm sạch các nguồn tài nguyên một cách kịp thời. Giải pháp băng thông cho loại vấn đề này là dynamic-wind, cho phép chúng tôi thêm các trình xử lý khi nhập và thoát vào một phạm vi khối. Vì vậy, chúng tôi có thể mở lại tệp khi khối được khởi động lại bằng cách tiếp tục. Và không chỉ mở lại, nhưng thực sự định vị luồng ở chính xác cùng vị trí trong tệp và cứ thế. Nếu luồng là một nửa thông qua giải mã một số ký tự UTF-8, chúng ta phải đặt nó vào cùng một trạng thái. Vì vậy, nếu Lisp được tiếp tục, hoặc là họ sẽ bị phá vỡ bởi các cấu trúc khác nhau để thực hiện dọn dẹp (tích hợp kém) hoặc những cấu trúc khác sẽ phải thu được nhiều ngữ nghĩa lông hơn nhiều.

Có các giải pháp thay thế để tiếp tục. Một số cách sử dụng tiếp tục là không cần thiết. Về cơ bản, cùng một tổ chức mã có thể thu được bằng cách đóng cửa hoặc khởi động lại. Ngoài ra, có một cấu trúc hệ điều hành/ngôn ngữ mạnh có thể cạnh tranh với sự tiếp tục: cụ thể là, chuỗi. Trong khi sự tiếp tục có các khía cạnh không được mô hình hóa độc đáo bởi các luồng (và chưa kể rằng chúng không giới thiệu các deadlocks và các điều kiện chủng tộc vào trong mã), chúng cũng có những nhược điểm so với các luồng như thiếu đồng thời thực tế cho việc sử dụng nhiều bộ xử lý, hoặc ưu tiên. Nhiều vấn đề có thể diễn đạt với sự tiếp tục có thể được diễn tả với các chủ đề gần như dễ dàng. Ví dụ, các phép tiếp tục cho phép chúng ta viết một trình phân tích cú pháp đệ quy, giống như một đối tượng giống như luồng mà chỉ trả về các kết quả tiến bộ khi nó phân tích cú pháp. Mã thực sự là một trình phân tích cú pháp gốc đệ quy và không phải là một máy trạng thái mô phỏng một. Các chủ đề cho phép chúng ta làm điều tương tự: chúng ta có thể đặt trình phân tích cú pháp vào một luồng được bao bọc trong một "đối tượng hoạt động", trong đó có một số phương thức "có được điều tiếp theo" để lấy thứ từ một hàng đợi.Khi các trình phân tích cú pháp luồng, thay vì trả về một sự tiếp tục, nó chỉ ném các đối tượng vào một hàng đợi (và có thể chặn một số luồng khác để loại bỏ chúng). Tiếp tục thực hiện được cung cấp bằng cách nối lại luồng đó; ngữ cảnh luồng của nó là sự tiếp tục. Không phải tất cả các mô hình luồng đều bị các điều kiện chủng tộc (nhiều); đó là ví dụ hợp tác luồng, theo đó một thread chạy tại một thời điểm, và chuyển đổi chủ đề chỉ có khả năng diễn ra khi một sợi làm cho một cuộc gọi rõ ràng vào hạt nhân luồng. Các triển khai chính của Lisp thường gặp có các chủ đề trọng lượng nhẹ (thường được gọi là "quy trình") trong nhiều thập kỷ và dần dần chuyển sang luồng phức tạp hơn với hỗ trợ đa xử lý. Sự hỗ trợ cho các chủ đề làm giảm nhu cầu tiếp tục, và là một ưu tiên thực hiện lớn hơn vì thời gian chạy ngôn ngữ không có hỗ trợ luồng là bất lợi về mặt công nghệ: không có khả năng tận dụng toàn bộ tài nguyên phần cứng.

3

Thiết kế Đề án dựa trên việc sử dụng các cuộc gọi hàm để thay thế các cấu trúc điều khiển phổ biến nhất. Đây là lý do tại sao Scheme yêu cầu loại bỏ cuộc gọi đuôi: nó cho phép một vòng lặp được chuyển đổi thành một cuộc gọi đệ quy mà không có khả năng chạy ra khỏi không gian ngăn xếp. Và cách tiếp cận cơ bản của việc này là kiểu chuyển tiếp liên tục.

Lisp thông thường thực tế hơn và ít mang tính sư phạm hơn. Nó không ra lệnh cho các chiến lược triển khai, và việc tiếp tục không bắt buộc phải thực hiện nó.

+5

Đề án là sư phạm và thực tế quá .. tốt nhất của cả hai thế giới;) – Ankur

2

Lisp thường là kết quả của một nỗ lực tiêu chuẩn hóa trên một số hương vị của thực tế (áp dụng) Lisps (do đó "phổ biến"). CL hướng đến các ứng dụng thực tế, do đó nó có nhiều tính năng "cụ thể" (như handler-bind) thay vì call/cc.

Đề án được thiết kế như một ngôn ngữ nhỏ gọn để dạy CS, do đó, nó có cơ bản call/cc có thể được sử dụng để triển khai các công cụ khác.

Xem thêm Can call-with-current-continuation be implemented only with lambdas and closures?

5

Đây là những gì Kent M. Pitman, một trong những nhà thiết kế của Common Lisp, phải nói về chủ đề này: from comp.lang.lisp

Các vấn đề liên quan