Xem xét chương trình Haskell sau đây. Tôi đang cố gắng để chương trình trong một "phong cách dòng", nơi chức năng hoạt động trên suối (thực hiện ở đây chỉ đơn giản là danh sách). Những thứ như normalStreamFunc hoạt động tốt với danh sách lười biếng. Tôi có thể chuyển một danh sách vô hạn tới normalStreamFunc và nhận ra một danh sách vô hạn khác, nhưng với một hàm được ánh xạ lên mỗi giá trị. Những thứ như effectfulStreamFunc không hoạt động tốt. Hành động IO có nghĩa là tôi cần đánh giá toàn bộ danh sách trước khi tôi có thể rút ra các giá trị riêng lẻ. Ví dụ, đầu ra của chương trình là:Luồng Haskell có hiệu ứng IO
a
b
c
d
"[\"a\",\"b\"]"
nhưng những gì tôi muốn là một cách để viết effectfulStreamFunc để chương trình sản xuất này:
a
b
"[\"a\",\"b\"]"
rời khỏi hành động còn lại unevaluated. Tôi có thể tưởng tượng một giải pháp bằng cách sử dụng unsafePerformIO, nhưng hãy nói rằng tôi lấy nó ra khỏi bảng. Đây là chương trình:
import IO
normalStreamFunc :: [String] -> [String]
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs
effectfulStreamFunc :: [String] -> IO [String]
effectfulStreamFunc [] = return []
effectfulStreamFunc (x:xs) = do
putStrLn x
rest <- effectfulStreamFunc xs
return (reverse(x):rest)
main :: IO()
main = do
let fns = ["a", "b", "c", "d"]
es <- effectfulStreamFunc fns
print $ show $ take 2 es
Cập nhật:
Cảm ơn tất cả các bạn cho ý kiến phản hồi hữu ích và chu đáo. Tôi đã không nhìn thấy các nhà điều hành sequence
trước đây, đó là hữu ích để biết về. Tôi đã nghĩ đến một cách (ít thanh lịch hơn) để vượt qua các giá trị IO (String) thay vì Strings, nhưng đối với kiểu lập trình có tính hữu dụng hạn chế, vì tôi muốn các hàm stream khác hành động trên các chuỗi, chứ không phải trên hành động có thể tạo ra một chuỗi. Nhưng, dựa trên suy nghĩ thông qua các phản ứng khác, tôi nghĩ rằng tôi thấy lý do tại sao điều này là không thể giải quyết nói chung. Trong trường hợp đơn giản tôi đã trình bày, điều tôi thực sự muốn là toán tử sequence
, vì tôi đã nghĩ rằng thứ tự luồng ngụ ý một thứ tự trên các hành động. Trong thực tế, không có thứ tự như vậy nhất thiết phải ngụ ý. Điều này trở nên rõ ràng hơn với tôi khi tôi nghĩ về một hàm luồng lấy hai luồng làm đầu vào (ví dụ: thêm hai lần hai luồng). Nếu cả hai luồng "đến" được thực hiện IO, thứ tự của các hành động IO đó là không xác định (trừ khi, tất nhiên, chúng ta định nghĩa nó bằng cách tự sắp xếp nó trong đơn nguyên IO). Đã giải quyết được vấn đề, cảm ơn tất cả!
Nếu bạn vẫn thích làm ký hiệu, bạn có thể viết các mệnh đề thứ hai của effectfulStreamFunc như: effectfulStreamFunc (x: xs) = let putAction = do putStrLn x return x in putAction: effectfulStreamFunc xs Tôi nghĩ rằng nó đọc tốt hơn một chút. –
Điểm tốt. Tôi đoán cú pháp làm là trực giao với vấn đề thực sự chạy monad. –