Tôi quan tâm đến việc khi nào và khi giá trị lớp "toàn cầu" đa hình "" được chia sẻ/ghi nhớ, đặc biệt là trên các ranh giới mô-đun. Tôi đã đọc this và this, nhưng chúng dường như không phản ánh hoàn cảnh của tôi và tôi thấy một số hành vi khác với những gì người ta có thể mong đợi từ câu trả lời.sử dụng lại/ghi nhớ các giá trị đa hình toàn cầu (lớp) trong Haskell
Hãy xem xét một lớp học đó cho thấy nhiều giá trị mà có thể tốn kém để tính toán:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module A
import Debug.Trace
class Costly a where
costly :: a
instance Num i => Costly i where
-- an expensive (but non-recursive) computation
costly = trace "costly!" $ (repeat 1) !! 10000000
foo :: Int
foo = costly + 1
costlyInt :: Int
costlyInt = costly
Và một module riêng biệt:
module B
import A
bar :: Int
bar = costly + 2
main = do
print foo
print bar
print costlyInt
print costlyInt
Chạy main
mang hai đánh giá riêng của costly
(như được chỉ ra bởi các dấu vết): một cho số foo
và một cho số bar
. Tôi biết rằng costlyInt
chỉ trả lại (được đánh giá) costly
từ foo
, bởi vì nếu tôi xóa print foo
từ main
thì costlyInt
đầu tiên trở nên tốn kém. (. Tôi cũng có thể gây ra costlyInt
để thực hiện một đánh giá riêng biệt không có vấn đề gì, bằng cách khái quát các loại foo
-Num a => a
)
Tôi nghĩ rằng tôi biết lý do tại sao hành vi này xảy ra: trường hợp của Costly
là một cách hiệu quả là một chức năng mà phải mất một Num
từ điển và tạo một từ điển Costly
. Vì vậy, khi biên dịch bar
và giải quyết tham chiếu đến costly
, ghc sẽ tạo một từ điển Costly
mới, có một phần lớn trong đó. Câu hỏi 1: Tôi có đúng về điều này không?
Có một vài cách để gây ra chỉ là một đánh giá costly
, bao gồm:
- Đặt tất cả mọi thứ trong một module.
- Xóa ràng buộc đối tượng
Num i
và chỉ cần xác định trường hợpCostly Int
.
Thật không may, các tương tự của các giải pháp này không khả thi trong chương trình của tôi - tôi có một số mô-đun sử dụng giá trị lớp trong dạng đa hình của nó và chỉ trong tệp nguồn cấp cao nhất.
Ngoài ra còn có sự thay đổi khi không giảm số lượng đánh giá, chẳng hạn như:
- Sử dụng INLINE, INLINABLE, hoặc NOINLINE trên
costly
định nghĩa trong ví dụ. (Tôi đã không mong đợi điều này để làm việc, nhưng hey, giá trị một shot.) - Sử dụng một
SPECIALIZE instance Costly Int
pragma trong định nghĩa cá thể.
Điều sau thật đáng ngạc nhiên đối với tôi - tôi cho rằng về cơ bản nó tương đương với mục thứ hai phía trên rằng đã hoạt động. Tức là, tôi nghĩ rằng nó sẽ tạo ra một từ điển đặc biệt Costly Int
, tất cả là foo
, bar
và costlyInt
sẽ chia sẻ. Câu hỏi của tôi 2: Tôi đang thiếu gì ở đây?
Câu hỏi cuối cùng của tôi: có cách nào tương đối đơn giản và dễ hiểu để nhận những gì tôi muốn, tức là tất cả các tham chiếu đến costly
của loại cụ thể được chia sẻ trên các mô-đun? Từ những gì tôi đã nhìn thấy cho đến nay, tôi nghi ngờ câu trả lời là không, nhưng tôi vẫn giữ hy vọng.
Câu hỏi này chính xác như thế nào so với [câu trước của tôi] (http://stackoverflow.com/questions/25057803/is-there-an-automatic-way-to-memoise-global-polymorphic-values-in- haskell)? – leftaroundabout
Từ những gì tôi có thể nói, chi phí trong câu hỏi của bạn xuất phát từ đệ quy kết hợp với việc chuyển từ điển, điều này gây ra sự đánh giá lại theo cấp số nhân. Một khi sau được cố định, tính toán chính nó là nhanh chóng. Nhưng nếu hai mô-đun được gọi là fibsImplicitDict, chúng sẽ nhận được các khối khác nhau sẽ được đánh giá riêng biệt. Tôi không thể đủ khả năng đó, bởi vì tính toán của tôi vốn dĩ đắt tiền (không phải do đệ quy). –
PS - thực tế là có những trường hợp lớp với các ràng buộc trên chúng cũng đóng một vai trò ở đây, có vẻ như. –