Có một ngôn ngữ chức năng cho phép sử dụng ngữ nghĩa ngăn xếp - hủy xác định tự động ở cuối phạm vi không?
Có một ngôn ngữ chức năng cho phép sử dụng ngữ nghĩa ngăn xếp - hủy xác định tự động ở cuối phạm vi không?
Câu trả lời:
Không phải tôi biết, mặc dù tôi không phải là chuyên gia lập trình chức năng.
Về nguyên tắc có vẻ khá khó khăn, bởi vì các giá trị được trả về từ các hàm có thể chứa các tham chiếu đến các giá trị khác được tạo (trên ngăn xếp) trong cùng một hàm hoặc có thể dễ dàng được truyền vào như một tham số hoặc được tham chiếu bởi một cái gì đó được truyền vào như một tham số. Trong C, vấn đề này được giải quyết bằng cách cho phép các con trỏ lơ lửng (hay chính xác hơn là hành vi không xác định) có thể xảy ra nếu lập trình viên không làm mọi thứ đúng. Đó không phải là loại giải pháp mà các nhà thiết kế ngôn ngữ chức năng tán thành.
Có những giải pháp tiềm năng, mặc dù. Một ý tưởng là làm cho tuổi thọ của giá trị trở thành một phần của loại giá trị, cùng với các tham chiếu đến nó và xác định các quy tắc dựa trên loại ngăn chặn các giá trị được cấp phát ngăn xếp được trả về hoặc được tham chiếu bởi thứ gì đó được trả về từ chức năng. Tôi đã không làm việc thông qua các hàm ý, nhưng tôi nghi ngờ nó sẽ là khủng khiếp.
Đối với mã đơn âm, cũng có một giải pháp khác (thực tế hoặc gần như) đơn nguyên, và có thể đưa ra một loại IORef tự động bị phá hủy xác định. Nguyên tắc là xác định các hành động "lồng". Khi được kết hợp (sử dụng toán tử kết hợp), các toán tử này xác định luồng điều khiển lồng nhau - tôi nghĩ "phần tử XML", với hầu hết các giá trị cung cấp cặp thẻ bắt đầu và kết thúc bên ngoài. Các "thẻ XML" này chỉ xác định thứ tự các hành động đơn điệu ở một mức độ trừu tượng khác.
Tại một số điểm (ở phía bên phải của chuỗi thành phần kết hợp), bạn cần một số loại đầu cuối để kết thúc việc làm tổ - một cái gì đó để lấp đầy lỗ hổng ở giữa. Nhu cầu về một bộ kết thúc là điều có lẽ có nghĩa là toán tử thành phần lồng nhau không đơn điệu, mặc dù vậy, tôi không hoàn toàn chắc chắn vì tôi đã không làm việc thông qua các chi tiết. Vì tất cả việc áp dụng terminator thực hiện là chuyển đổi một hành động lồng nhau thành một hành động đơn điệu bình thường có hiệu quả, có thể không - nó không nhất thiết ảnh hưởng đến toán tử thành phần lồng.
Nhiều hành động đặc biệt này sẽ có bước "thẻ kết thúc" null và sẽ đánh đồng bước "thẻ bắt đầu" với một số hành động đơn điệu đơn giản. Nhưng một số sẽ đại diện cho khai báo biến. Chúng sẽ đại diện cho hàm tạo với thẻ bắt đầu và hàm hủy với thẻ kết thúc. Vì vậy, bạn nhận được một cái gì đó như ...
act = terminate ((def-var "hello" ) >>>= \h ->
(def-var " world") >>>= \w ->
(use-val ((get h) ++ (get w)))
)
Dịch sang một tác phẩm đơn âm theo thứ tự thực hiện sau, mỗi thẻ (không phải phần tử) trở thành một hành động đơn âm bình thường ...
<def-var val="hello"> -- construction
<def-var val=" world> -- construction
<use-val ...>
<terminator/>
</use-val> -- do nothing
</def-val> -- destruction
</def-val> -- destruction
Các quy tắc như thế này có thể cho phép thực hiện RAII theo kiểu C ++. Các tham chiếu giống IORef không thể thoát khỏi phạm vi của chúng, vì những lý do tương tự như lý do tại sao các IORef bình thường không thể thoát khỏi đơn nguyên - các quy tắc của thành phần kết hợp không cung cấp cách nào để tham chiếu thoát ra.
EDIT - Tôi gần như quên nói - có một khu vực nhất định tôi không chắc chắn về đây. Về cơ bản, điều quan trọng là phải đảm bảo rằng một biến bên ngoài không thể tham chiếu một biến bên trong, do đó, phải có những hạn chế đối với những gì bạn có thể làm với các tham chiếu giống IORef này. Một lần nữa, tôi đã không làm việc thông qua tất cả các chi tiết.
Do đó, việc xây dựng có thể mở một tập tin phá hủy. Xây dựng có thể mở một ổ cắm mà phá hủy đóng cửa. Về cơ bản, như trong C ++, các biến trở thành người quản lý tài nguyên. Nhưng không giống như C ++, không có đối tượng phân bổ heap không thể tự động bị hủy.
Mặc dù cấu trúc này hỗ trợ RAII, bạn vẫn cần một trình thu gom rác. Mặc dù một hành động lồng nhau có thể phân bổ và giải phóng bộ nhớ, coi nó như một tài nguyên, vẫn có tất cả các tham chiếu đến các giá trị chức năng (có khả năng được chia sẻ) trong khối bộ nhớ đó và các nơi khác. Cho rằng bộ nhớ có thể được phân bổ đơn giản trên ngăn xếp, tránh sự cần thiết của một đống miễn phí, ý nghĩa thực sự (nếu có) là đối với các loại quản lý tài nguyên khác.
Vì vậy, điều này đạt được là tách biệt quản lý tài nguyên theo kiểu RAII khỏi quản lý bộ nhớ, ít nhất là trong trường hợp RAII dựa trên phạm vi lồng đơn giản. Bạn vẫn cần một trình thu gom rác để quản lý bộ nhớ, nhưng bạn có thể dọn sạch các tài nguyên khác một cách an toàn và kịp thời.
shared_ptr<>
), bạn vẫn tiếp tục phá hủy xác định. Một điều khó khăn cho RAII là các tài liệu tham khảo theo chu kỳ; RAII hoạt động sạch sẽ nếu đồ thị quyền sở hữu là đồ thị theo chu kỳ có hướng.
Nếu bạn coi C ++ là ngôn ngữ chức năng (nó có lambdas), thì đó là một ví dụ về ngôn ngữ không sử dụng bộ sưu tập rác.
Tôi phải nói rằng câu hỏi hơi khó hiểu vì nó giả định rằng có một bộ sưu tập "ngôn ngữ chức năng" tiêu chuẩn. Hầu như mọi ngôn ngữ lập trình đều hỗ trợ một số lượng lập trình chức năng. Và, hầu như mọi ngôn ngữ lập trình đều hỗ trợ một số lượng lập trình bắt buộc. Trường hợp nào người ta vẽ đường để nói đó là ngôn ngữ chức năng và ngôn ngữ bắt buộc, ngoài hướng dẫn bởi định kiến văn hóa và giáo điều phổ biến?
Một cách tốt hơn để diễn đạt câu hỏi sẽ là "có thể hỗ trợ lập trình chức năng trong một bộ nhớ được cấp phát ngăn xếp". Câu trả lời là, như đã đề cập, rất khó. Phong cách lập trình chức năng thúc đẩy việc phân bổ các cấu trúc dữ liệu đệ quy theo ý muốn, đòi hỏi một bộ nhớ heap (cho dù rác được thu thập hoặc tham chiếu được tính). Tuy nhiên, có một kỹ thuật phân tích trình biên dịch khá phức tạp được gọi là phân tích bộ nhớ dựa trên khu vực , trong đó trình biên dịch có thể chia heap thành các khối lớn có thể được phân bổ và phân bổ tự động, theo cách tương tự như phân bổ ngăn xếp. Trang Wikipedia liệt kê các triển khai khác nhau của kỹ thuật, cho cả hai ngôn ngữ "chức năng" và "mệnh lệnh".