2013-06-17 30 views
7

Hôm nay tôi muốn điều tra nếu có thể xây dựng kiểu dữ liệu theo cách sao cho nó không lưu trữ dữ liệu kiểu chữ ký của nó, nhưng đại diện khác của nó . Vì vậy, đây là nỗ lực của tôi về một GADT trong đó có một nhà xây dựng kiểu của loại a, nhưng một nhà xây dựng dữ liệu của loại ByteString.Ví dụ Functor cho GADT có ràng buộc kiểu

{-# LANGUAGE GADTs #-} 
import Data.ByteString.Char8 
import Data.Serialize 

data Serialized a where 
    MkSerialized :: (Serialize a) => ByteString -> Serialized a 

Bây giờ tôi có thể định nghĩa một hàm decode' theo cách sau:

decode' :: (Serialize a) => Serialized a -> a 
decode' (MkSerialized bs) = let Right r = (decode bs) in r 

Và nó hoạt động:

let s = MkSerialized (encode "test") :: Serialized String 
print $ decode' s  -- prints "test" 

Vấn đề của tôi bây giờ là tôi muốn trở thành một Serialized ví dụ của Functor.

instance Functor Serialized where 
    fmap f (MkSerialized bs) = MkSerialized (encode (f (right (decode bs)))) 
           where right (Right r) = r 

Nhưng tôi gặp lỗi (Serialize b) không thể suy luận được. Làm thế nào tôi có thể hạn chế cá thể Functor để Serialize được thực thi trong fmap?

+2

Bạn không thể. 'Functor' không cho phép các ràng buộc về các tham số kiểu được yêu cầu. Có một lớp functor bị hạn chế, ['RFunctor'] (http://hackage.haskell.org/packages/archive/rmonad/0.8/doc/html/Control-RMonad.html#t:RFunctor) trong gói' rmonad' . Có lẽ bạn có thể sử dụng nó. –

+2

Điều này không liên quan đến câu hỏi của bạn - điều này thực sự là không thể với 'Functor' - nhưng tôi cảm thấy có nghĩa vụ phải đề cập đến: Vui lòng không sử dụng' Data.ByteString.Char8' theo mặc định! Đó là một mô-đun bị hỏng khuyến khích mã bị hỏng. Đôi khi có một số cách sử dụng, nhưng mã của bạn hoạt động tốt với 'Data.ByteString', điều này không khuyến khích hiểu lầm về Unicode. – shachaf

+1

Đối với những gì nó có giá trị, bạn có thể tạo kiểu dữ liệu kiểu 'CoYoneda' như' dữ liệu được nối tiếp một nơi mà MkSerialized :: Serialize x => ByteString -> (x -> a) -> Nối tiếp a' lưu trữ một ByteString và một chức năng post-deserialization, và nó có một cá thể 'Functor'. Nhưng tất nhiên là đánh bại mục đích ở đây. – shachaf

Trả lời

6

Bạn có thể thực hiện việc này bằng cách sử dụng CoYoneda functor.

Ý tưởng rất đơn giản: có trường chức năng bổ sung nơi bạn tích lũy các hàm ing fmap. Khi bạn giải mã giá trị của mình, hãy áp dụng hàm đó.

Dưới đây là các mã:

{-# LANGUAGE GADTs #-} 
import Data.ByteString.Char8 
import Data.Serialize 

data Serialized a where 
    MkSerialized 
     :: (Serialize a) 
     => ByteString -> (a -> b) -> Serialized b 

decode' :: Serialized a -> a 
decode' (MkSerialized bs f) = let Right r = decode bs in f r 

instance Functor Serialized where 
    fmap f (MkSerialized bs g) = MkSerialized bs (f . g) 

này cũng có lợi ích của tự động pha trộn nhiều fmap s thay vì decodings lặp đi lặp lại và mã hóa, như sẽ là trong trường hợp của bạn.

+2

Trong khi điều này không thực sự giải quyết vấn đề của tôi (vì tôi đã thích 'fmap' để lặp lại mã hóa), tôi sẽ chấp nhận câu trả lời này b/c Tôi thấy ý tưởng ban đầu của tôi là không thể và đây là cách thiết thực nhất để định nghĩa một hàm functor cho một GADT bị ràng buộc. Ngoài ra, [đọc thú vị cho GADTs và Yoneda functors] (http://www.haskellforall.com/2012/06/gadts.html). – Phae7rae

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