Tôi sẽ cố gắng minh họa cách tiếp cận của Haskell (Tôi không chắc rằng trực giác của tôi là chính xác 100% vì tôi không phải là chuyên gia của Haskell, việc sửa chữa được hoan nghênh).
Mã của bạn có thể được viết bằng Haskell như sau:
import System.CPUTime
f :: Integer -> Integer -> IO Integer
f a b = do
t <- getCPUTime
return (a + b + (div t 1000000000000))
Vậy, minh bạch tham chiếu ở đâu?
f
là một hàm, được đưa ra hai số nguyên a
và b
, sẽ tạo ra một hành động, như bạn có thể biết bằng kiểu trả về IO Integer
. Hành động này sẽ luôn giống nhau, được đưa ra hai số nguyên, do đó, hàm ánh xạ một cặp số nguyên cho các hành động IO được minh bạch tham chiếu.
Khi hành động này được thực thi, giá trị nguyên mà nó tạo ra sẽ phụ thuộc vào thời gian CPU hiện tại: thực thi các hành động KHÔNG phải là ứng dụng chức năng.
Tóm tắt: Trong Haskell, bạn có thể sử dụng các hàm thuần túy để xây dựng và kết hợp các hành động phức tạp (giải trình tự, soạn thảo hành động, v.v.) theo cách minh bạch tham chiếu. Một lần nữa, lưu ý rằng trong ví dụ trên, hàm thuần f
không trả về một số nguyên: nó trả về một hành động.
BIÊN TẬP
Một số chi tiết liên quan đến câu hỏi JohnDoDo.
Điều đó có nghĩa là "thực thi hành động KHÔNG phải là ứng dụng chức năng"?
Cho các tập hợp T1, T2, Tn, T, một hàm f là ánh xạ (quan hệ) liên kết với từng bộ trong T1 x T2 x ... x Tn một giá trị trong T. Vì vậy, ứng dụng hàm tạo ra một giá trị đầu ra cho một số giá trị đầu vào . Sử dụng cơ chế này, bạn có thể xây dựng các biểu thức đánh giá thành các giá trị, ví dụ giá trị 10
là kết quả của việc đánh giá biểu thức 4 + 6
. Lưu ý rằng, khi ánh xạ giá trị thành giá trị theo cách này, bạn không thực hiện bất kỳ loại đầu vào / đầu ra nào.
Trong Haskell, các hành động là các giá trị của các loại đặc biệt có thể được xây dựng bằng cách đánh giá các biểu thức có chứa các hàm thuần phù hợp hoạt động với các hành động. Theo cách này, chương trình Haskell là một hành động tổng hợp có được bằng cách đánh giá main
hàm. Hành động chính này có loại IO ()
.
Khi hành động tổng hợp này đã được xác định, một cơ chế khác (không phải ứng dụng chức năng) được sử dụng để gọi / thực hiện hành động (xem ví dụ ở đây ). Toàn bộ chương trình thực hiện là kết quả của việc gọi hành động chính có thể lần lượt gọi các hành động phụ. Cơ chế gọi này (có chi tiết nội bộ mà tôi không biết) đảm nhiệm việc thực hiện tất cả các cuộc gọi IO cần thiết, có thể truy cập vào thiết bị đầu cuối, đĩa, mạng, v.v.
Quay trở lại ví dụ. Hàm f
trên không trả về một số nguyên và bạn không thể viết hàm thực hiện IO và trả về một số nguyên cùng một lúc: bạn phải chọn một trong hai số nguyên.
Những gì bạn có thể làm là nhúng hành động được trả về f 2 3
vào một hành động phức tạp hơn. Ví dụ: nếu bạn muốn in số nguyên được tạo bởi hành động đó, bạn có thể viết:
main :: IO ()
main = do
x <- f 2 3
putStrLn (show x)
Các do
ký hiệu chỉ ra rằng hành động trả về bởi các chức năng chính là thu được bằng một phần tuần tự của hai hành động nhỏ hơn, và các x <-
ký hiệu chỉ ra rằng giá trị sản xuất trong hành động đầu tiên phải được thông qua với hành động thứ hai.
Trong hành động thứ hai
putStrLn (show x)
tên x
được liên kết với số nguyên được tạo ra bằng cách thực hiện hành động
f 2 3
Một điểm quan trọng là số nguyên được tạo ra khi hành động đầu tiên được gọi chỉ có thể sống bên trong các hành động IO: nó có thể được truyền từ một hành động IO sang hành động tiếp theo nhưng nó không thể được trích xuất dưới dạng giá trị số nguyên.
So sánh main
chức năng trên với cái này:
main = do
let y = 2 + 3
putStrLn (show y)
Trong trường hợp này, chỉ có một hành động, cụ thể putStrLn (show y)
, và y
bị ràng buộc với kết quả của việc áp dụng hàm thuần túy +
. Chúng ta cũng có thể định nghĩa hành động chính này như sau:
main = putStrLn "5"
Vì vậy, hãy chú ý cú pháp khác nhau
x <- f 2 3 -- Inject the value produced by an action into
-- the following IO actions.
-- The value may depend on when the action is
-- actually executed. What happens when the action is
-- executed is not known here: it may get user input,
-- access the disk, the network, the system clock, etc.
let y = 2 + 3 -- Bind y to the result of applying the pure function `+`
-- to the arguments 2 and 3.
-- The value depends only on the arguments 2 and 3.
Tóm lược
- Trong Haskell, các hàm thuần túy được sử dụng để xây dựng các hành động tạo thành một chương trình.
- Hành động là giá trị của một loại đặc biệt.
- Vì các hành động được xây dựng bằng cách áp dụng các hàm thuần túy, nên hành động xây dựng được minh bạch.
- Sau khi một hành động đã được xây dựng, nó có thể được gọi bằng một cơ chế riêng.