2011-12-26 29 views
7

Tôi đang đọc qua LYAH, và trong Chương 9, tôi đã tìm thấy một vấn đề tò mò. Tác giả cung cấp ví dụ về việc triển khai chức năng "randoms":Haskell: Thực hiện "randoms" (a.k.a., Biến loại mơ hồ)

randoms' :: (RandomGen g, Random a) => g -> [a] 
randoms' gen = let (value, newGen) = random gen in value:randoms' newGen 

Vâng, điều này chỉ tốt thôi. Nhưng nếu tôi thay đổi dòng thứ hai để:

randoms' gen = (fst (random gen)) : (randoms' (snd (random gen))) 

Các lỗi này báo cáo tập tin trên tải:

IOlesson.hs:4:52: 
    Ambiguous type variable `a' in the constraint: 
     `Random a' arising from a use of `random' at IOlesson.hs:4:52-61 
    Probable fix: add a type signature that fixes these type variable(s) 
Failed, modules loaded: none. 

Nếu tôi thay đổi dòng này để:

randoms' gen = (fst (random gen)) : (randoms' gen) 

Sau đó, điều này sẽ làm chỉ tốt, và như mong đợi, điều này sẽ trả về một danh sách của tất cả các yếu tố giống hệt nhau.

Tôi đang bối rối: Có gì khác biệt trong phiên bản của Miran và phiên bản của tôi?

Cảm ơn mọi ý tưởng!

Trả lời

7

Vấn đề là random lấy bất kỳ phiên bản nào của RandGen và trả về giá trị ngẫu nhiên và trình tạo mới cùng loại. Nhưng giá trị ngẫu nhiên có thể là bất kỳ loại nào với phiên bản Random!

random :: (Random a, RandomGen g) => g -> (a, g) 

Vì vậy, khi bạn gọi random lần thứ hai trong đệ quy, nó không biết những gì loại của phần tử đầu tiên nên được! Đúng, bạn không thực sự quan tâm đến nó (bạn vứt nó đi với snd, sau khi tất cả), nhưng sự lựa chọn của a có thể ảnh hưởng đến hành vi của random. Vì vậy, để phân biệt, bạn cần phải nói với GHC những gì bạn muốn một được. Cách đơn giản nhất là để viết lại định nghĩa của bạn như sau:

randoms' gen = let (value, gen') = random gen in value : randoms' gen' 

Bởi vì bạn sử dụng value như là một phần của danh sách kết quả, nó buộc phải có các loại tương tự như một trong chữ ký kiểu của bạn - các loại nguyên tố của danh sách kết quả. Sự mơ hồ được giải quyết, và tính toán trùng lặp của số ngẫu nhiên tiếp theo được tránh, để khởi động. Có nhiều cách để định hướng trực tiếp hơn (giữ tính toán trùng lặp), nhưng chúng hoặc là xấu xí và khó hiểu hoặc liên quan đến các phần mở rộng ngôn ngữ. Rất may, bạn không nên chạy vào điều này rất thường xuyên, và khi bạn làm, một phương pháp như thế này sẽ làm việc để giải quyết sự mơ hồ.

Tương và có lẽ gọn gàng hơn, bạn có thể viết:

randoms' gen = value : randoms' gen' 
    where (value, gen') = random gen 
+0

Cảm ơn! Điều này là để truy cập trực quan, nhưng hoàn toàn dễ hiểu. –

+0

Bạn được chào đón; lỗi mơ hồ về lỗi chính tả có thể khó khăn lúc đầu, nhưng bạn sẽ sớm nhận được lỗi đó :) – ehird

4

Hãy xem xét các loại random:

random :: (RandomGen g, Random a) => g -> (a, g) 

Kết quả tuple bao gồm một giá trị bất kỳ loại đó là một thể hiện của Random, và giá trị RNG được cập nhật. Phần quan trọng là "bất kỳ cá thể nào": không có gì yêu cầu cả việc sử dụng random để tạo ra một giá trị cùng loại.

Trong fst (random gen) không có vấn đề gì, bởi vì giá trị được tạo ra là bất kỳ loại nào mà toàn bộ chức năng cần; Tuy nhiên, trong snd (random gen), giá trị ngẫu nhiên bị bỏ đi, do đó, nó hoàn toàn không biết loại đó sẽ là gì. Nếu không biết loại, Haskell không thể chọn trường hợp Random thích hợp để sử dụng, do đó lỗi loại mơ hồ mà bạn nhìn thấy.

1

random là loại: RandomGen g => g -> (a, g)

và do đó snd (random gen) là duy nhất của loại g -> g. Và sau đó nó không biết a là gì. Có một khác nhau random cho mỗi loại dữ liệu bạn có thể muốn tạo ra, nhưng trong trường hợp này trình biên dịch không biết bạn có muốn một số random :: g -> (Int,g) hoặc random :: g->(Char,g) hay cái gì khác.

Điều này giải thích whey (value, newGen) = random gen hoạt động. Nó giúp trình biên dịch liên kết với nhau, đó là kiến ​​thức về những gì a là. value phải thuộc loại a và do đó nó có thể suy ra loại random gen.

(Sửa:. Tôi đã xóa một nỗ lực sai tôi thực hiện tại sửa chữa nó Chỉ cần gắn bó với mã gốc trong các câu hỏi!)

+1

Sửa lỗi đó yêu cầu '{- # LANGUAGE ScopedTypeVariables # -}' và 'forall' rõ ràng trong chữ ký loại. – ehird

+0

(Tôi ở xa chuyên gia về chi tiết của phần mở rộng là gì và không phải là gì) Tôi ngạc nhiên vì @ehird, biến kiểu 'a' đã có chữ ký kiểu rồi. Tôi chỉ sử dụng điều này để xác định rõ ràng loại biểu thức 'gen ngẫu nhiên'. Tôi đã nghĩ rằng đây là Haskell rất chuẩn. –

+0

Trong tiêu chuẩn Haskell, bất cứ khi nào bạn sử dụng một biến kiểu sau khi một '::' nó nằm trong một phạm vi mới. Nó rất ngớ ngẩn. – ehird