2017-12-09 29 views
6

Tôi đã chơi với Cloud Haskell. Tôi đã nhận thấy in the hackage documentation có một loại giao diện ứng dụng. Nhưng đặc biệt là tôi đang cố gắng tìm hoặc viết một hàm closurePure với chữ ký sau:Cloud Haskell - Cách viết "thuần túy" cho Đóng cửa?

closurePure :: (Typeable a, Binary a) => a -> Closure a 

Đây là phiên bản giới hạn của thuần túy.

Trong khi Closure datatype chính nó là trừu tượng, closure sau cung cấp:

closure :: Static (ByteString -> a) -> ByteString -> Closure a 

Vì vậy, tôi có thể có được điều này cho đến nay:

closurePure :: (Typeable a, Binary a) => a -> Closure a 
closurePure x = closure ??? (encode x) 

Vấn đề là những gì để đặt nơi ??? s là.

nỗ lực đầu tiên của tôi là như sau:

myDecode :: (Typeable a, Binary a) => Static (ByteString -> a) 
myDecode = staticPtr (static decode) 

Nhưng khi đọc GHC docs on static pointers, ví dụ show gợi ý với tôi rằng bạn không thể có một hạn chế vì một chức năng hạn chế không có một trường hợp Typeable. Vì vậy, tôi đã cố gắng làm việc xung quanh đề nghị sử dụng Dict:

myDecode :: Typeable a => Static (Dict (Binary a) -> ByteString -> a) 
myDecode = staticPtr (static (\Dict -> decode)) 

Nhưng bây giờ tôi đã có loại sai mà không phù hợp với closure chức năng trên.

Có cách nào để viết closurePure hoặc một cái gì đó tương tự (hoặc tôi đã bỏ lỡ nó trong tài liệu Cloud Haskell) không? Nâng các loại đồng bằng binary lên Closure s có vẻ cần thiết khi sử dụng giao diện được áp dụng, nhưng tôi không thể tìm ra cách thực hiện.

Lưu ý rằng tôi có thể làm điều này:

class StaticDecode a where 
    staticPtrDecode :: StaticPtr (ByteString -> a) 

instance StaticDecode Int where 
    staticPtrDecode = static Data.Binary.decode 

instance StaticDecode Float where 
    staticPtrDecode = static Data.Binary.decode 

instance StaticDecode Integer where 
    staticPtrDecode = static Data.Binary.decode 

-- More instances etc... 

myPure :: forall a. (Typeable a, StaticDecode a, Binary a) => a -> Closure a 
myPure x = closure (staticPtr staticPtrDecode) (encode x) 

nào hoạt động tốt nhưng về cơ bản đòi hỏi tôi phải lặp lại một ví dụ cho mỗi Binary dụ. Có vẻ như lộn xộn và tôi thích cách khác.

+0

Bạn có chắc 'myDecode = staticPtr (giải mã tĩnh)' không hoạt động không? Dường như với tôi rằng bạn vừa giao dịch ràng buộc 'Binary' cho một ràng buộc' StaticDecode', nhưng bạn nói nó đang hoạt động. – 4castle

+0

Xin lỗi, hàm cuối cùng nên được gọi là 'myPure', không phải' myDecode'. Hy vọng rằng có ý nghĩa hơn. Nhưng theo nghĩa bạn đúng, nhưng bên trong thể hiện 'StaticDecode' tôi gọi tĩnh (hoạt động) nhưng khi tôi lấy' decode' đầu tiên từ thể hiện nhị phân thì áp dụng static nó không thành công. Tôi đoán (điều này hoàn toàn là phỏng đoán) rằng mỗi cá thể của 'StaticDecode' tạo ra một con trỏ tĩnh duy nhất để nó hoạt động tốt. – Clinton

Trả lời

4

Bạn nói đúng, Closure có applicative- cấu trúc như, một thực tế khiến thậm chí rõ ràng hơn trong cả giao diện và thực hiện các distributed-closure. Nó không hoàn toàn áp dụng, bởi vì trong trường hợp pure chúng ta có ràng buộc bổ sung rằng đối số phải bằng cách nào đó có thể được tuần tự hóa.

Thực ra, chúng tôi có hạn chế mạnh hơn. Không chỉ đối số có thể được tuần tự hóa, nhưng ràng buộc phải tự nó được nối tiếp. Cũng giống như rất khó để sắp xếp từng hàm một cách trực tiếp, bạn có thể tưởng tượng rằng khó có thể serialize các ràng buộc. Nhưng cũng giống như đối với các chức năng, mẹo là để tuần tự hóa một con trỏ tĩnh tĩnh tới chính ràng buộc đó, nếu một con trỏ tĩnh tồn tại. Làm thế nào để chúng ta biết rằng một con trỏ như vậy tồn tại? Chúng tôi có thể giới thiệu một lớp kiểu với một phương pháp nào có thể cho chúng tôi biết tên của con trỏ, đưa ra một hạn chế:

class GimmeStaticPtr c where 
    gimmeStaticPtr :: StaticPtr (Dict c) 

Có một mẹo kỹ thuật nhẹ xảy ra ở đây. Loại chỉ mục loại cho StaticPtr là loại *, trong khi một ràng buộc có loại Constraint. Vì vậy, chúng tôi sử dụng lại một mẹo từ thư viện constraints bao gồm việc bao gồm ràng buộc vào một loại dữ liệu (Dict ở trên), giống như tất cả các loại dữ liệu thuộc loại *. Các ràng buộc có một phiên bản GimmeStaticPtr được liên kết được gọi là các ràng buộc tĩnh.

Nói chung, đôi khi hữu ích khi soạn các ràng buộc tĩnh để có được nhiều ràng buộc tĩnh hơn. StaticPtr không phải là composable, nhưng Closure là.vì vậy những gì distributed-closure thực sự làm là xác định một lớp học tương tự, rằng chúng ta sẽ gọi,

class GimmeClosure c where 
    gimmeClosure :: Closure (Dict c) 

Bây giờ chúng ta có thể xác định closurePure theo một cách tương tự mà bạn đã làm:

closurePure :: (Typeable a, GimmeClosure (Binary a)) => a -> Closure a 

Nó sẽ là tuyệt vời nếu trong tương lai, trình biên dịch có thể giải quyết các ràng buộc GimmeClosure khi đang chạy bằng cách tạo các con trỏ tĩnh khi cần. Nhưng hiện tại, thứ gần nhất là Mẫu Haskell. phân phối-đóng cửa cung cấp một mô-đun để autogenerate GimmeClosure (Cls a) khó khăn tại các trang web định nghĩa cho lớp Cls. Xem withStatichere.

Ngẫu nhiên, Edsko de Vries đã đưa ra một số great talk về đóng cửa phân phối và ý tưởng thể hiện trong đó.

2

Hãy dành một chút thời gian để xem xét những gì bạn đang yêu cầu. Nhớ lại rằng typeclasses về cơ bản là viết tắt của từ điển. Vì vậy, hãy viết lại:

data BinaryDict a = BinaryDict 
    { bdEncode :: a -> ByteString 
    , bdDecode :: ByteString -> a 
    } 

Bây giờ bạn muốn viết một hàm:

closurePure :: (Typeable a) => BinaryDict a -> a -> Closure a 

nỗ lực của bạn là:

closurePure bdict = closure (staticPtr (static (bdDecode bdict))) . bdEncode bdict 

Bây giờ chúng ta có thể nhìn thấy những gì đang xảy ra một cách rõ ràng, chúng ta có thể thấy không thể đóng đối số của đối số static. Nếu BinaryDict s được phép tạo willy nilly, nói từ dữ liệu người dùng, chức năng này sẽ là không thể. Thay vào đó, chúng tôi cần:

closurePure :: (Typeable a) => Static (BinaryDict a) -> a -> Closure a 

Tức là, chúng tôi cần mục nhập cho các trường hợp cần thiết Binary trong bảng con trỏ tĩnh. Do đó giải pháp điều tra của bạn, và tại sao tôi nghi ngờ rằng một giải pháp như vậy là bắt buộc.Chúng tôi cũng không thể tự mình liệt kê nó quá tự động, bởi vì có vô số trường hợp.

Điều này có vẻ ngớ ngẩn đối với tôi, tuy nhiên, vì các trường hợp dường như chỉ là những thứ bạn muốn tự động tĩnh. Họ là tĩnh bởi bản chất (đó là gì, reflection? Tôi không thể nghe thấy bạn). Điều này có lẽ ít nhất đã được ruminated về trong các giấy tờ Haskell phân phối (tôi đã không đọc chúng).

Chúng tôi có thể giải quyết vấn đề này nói chung bằng cách tạo một lớp cụ thể liệt kê mọi trường hợp của mỗi lớp (déjà vu?).

class c => StaticConstraint c where 
    staticConstraint :: StaticPtr (Dict c) 
instance StaticConstraint (Show Int) where 
    staticConstraint = static Dict 
-- a handful more lines... 

Hơi nhiều cách nghiêm túc, nếu bạn thực sự không muốn liệt kê (Tôi không đổ lỗi cho bạn), bạn ít nhất có thể xoa dịu nỗi đau với một quy ước gọi:

closurePure :: (Typeable a, Binary a) => StaticPtr (ByteString -> a) -> a -> Closure a 
closurePure decodePtr = closure (staticPtr decodePtr) . encode 

someClosure :: Closure Int 
someClosure = closurePure (static decode) 42 

vô nghĩa này là cần thiết vì static là "dạng cú pháp" chứ không phải là hàm - bằng cách đề cập đến nó, chúng tôi chỉ ra rằng cá thể Binary cho Int phải được tạo và ghi lại trong bảng con trỏ tĩnh.

Nếu bạn đang cảm thấy táo bạo, bạn có thể kích hoạt tính năng {-# LANGUAGE CPP #-}

-- PURE :: (Binary a, Typeable a) => a -> Closure a, I promise 
#define PURE (closurePure (static decode)) 

someClosure :: Closure Int 
someClosure = PURE 42 

Có lẽ một ngày nào đó Haskell sẽ thực hiện bước tiếp theo và sau đại học với thời gian thử nghiệm Segmentation fault (core dumped) của những người tiền nhiệm của nó thay vì phun ra những lỗi kiểu kiêu ngạo.

Các vấn đề liên quan