2011-11-04 29 views
17

Hãy xem xét các loại sau đây:Có thể tạo kiểu một thể hiện của một lớp nếu các tham số kiểu của nó không đúng thứ tự không?

data SomeType m a = SomeType (m Integer) [a] 

Chúng ta có thể dễ dàng thực hiện kiểu đó một thể hiện của functor với đoạn mã sau:

instance Functor (SomeType m) where 
    fmap f (SomeType m lst) = SomeType m (map f lst) 

Tuy nhiên, nếu thay vào đó params loại SomeType được hoán đổi:

data SomeType2 a m = SomeType2 (m Integer) [a] 

Sau đó, định nghĩa mẫu trên không hoạt động.

Có cách nào để tạo SomeType2 một phiên bản của Functor không? Nếu không, có bất kỳ bổ sung thêm và sắp tới để haskell/ghc mà có thể làm cho nó có thể?

Trả lời

20

Có thiên vị, nhưng tôi nghĩ đây là cơ hội tuyệt vời để sử dụng Control.Newtype, một phần nhỏ của bộ đó chỉ là "cabal install newtype".

Đây là giao dịch. Bạn muốn lật xung quanh các nhà xây dựng loại để có được bàn tay của bạn trên functoriality (ví dụ) trong một tham số khác nhau. Xác định một Newtype

newtype Flip f x y = Flip (f y x) 

và thêm nó vào lớp Newtype do đó

instance Newtype (Flip f x y) (f y x) where 
    pack = Flip 
    unpack (Flip z) = z 

Lớp Newtype chỉ là một newtypes sắp xếp thư mục để tương đương mảy may của họ, cung cấp bộ tiện dụng, ví dụ op Flip là nghịch đảo của Flip: bạn không cần nhớ những gì bạn gọi nó.

Đối với các vấn đề trong câu hỏi, bây giờ chúng ta có thể làm những thứ như thế này:

data Bif x y = BNil | BCons x y (Bif x y) deriving Show 

Đó là một kiểu dữ liệu hai tham số đó sẽ xảy ra là functorial trong cả hai tham số. (Có lẽ, chúng ta nên làm cho nó một thể hiện của một lớp Bifunctor, nhưng dù sao ...) Chúng ta có thể làm cho nó một Functor hai lần trở lên: một lần cho các tham số cuối cùng ...

instance Functor (Bif x) where 
    fmap f BNil = BNil 
    fmap f (BCons x y b) = BCons x (f y) (fmap f b) 

... và một lần cho là người đầu tiên:

instance Functor (Flip Bif y) where 
    fmap f (Flip BNil) = Flip BNil 
    fmap f (Flip (BCons x y b)) = Flip (BCons (f x) y (under Flip (fmap f) b)) 

nơi under p f là một cách gọn gàng để nói op p . f . p.

Tôi cho bạn biết không có lời nói dối: chúng ta hãy thử.

someBif :: Bif Int Char 
someBif = BCons 1 'a' (BCons 2 'b' (BCons 3 'c' BNil)) 

và sau đó chúng tôi nhận

*Flip> fmap succ someBif 
BCons 1 'b' (BCons 2 'c' (BCons 3 'd' BNil)) 
*Flip> under Flip (fmap succ) someBif 
BCons 2 'a' (BCons 3 'b' (BCons 4 'c' BNil)) 

Trong những trường hợp này, có thực sự rất nhiều cách điều tương tự có thể được xem như một Functor, vì vậy nó là đúng rằng chúng ta phải thực hiện một số tiếng ồn nói mà theo cách của chúng tôi. Nhưng tiếng ồn không phải là tất cả những gì nhiều nếu bạn có hệ thống về nó.

+0

kiểu mới rất tuyệt. Đó là một trong những gói mà, khi tôi lần đầu tiên nhìn thấy nó, tôi muốn tôi được sử dụng nó trong tất cả các công việc trước đây của tôi. –

+0

Trong khi điều này là mát mẻ và hoạt động, nó không thực sự trả lời câu hỏi vì bạn vẫn không thể tạo một thể hiện của Functor cho SomeType2 trực tiếp (và chỉ sử dụng fmap mà không có hàm 'under' wrapper). – ivanm

+2

Câu trả lời cho câu hỏi ban đầu là "không". Hoặc ít nhất, không phải trong Haskell. Ở một mức độ nào đó, nó sẽ là đặc biệt để trình bày một cách giải quyết có câu trả lời là "có". – pigworker

3

Điều này là không thể, và tôi không nghĩ rằng nó sẽ được bất cứ lúc nào sớm.

Việc sắp xếp các thông số kiểu là quan trọng. Giá trị cuối cùng là giá trị mà bạn sẽ "chứa" để sử dụng với Functor, v.v.

Tôi đã cố gắng làm việc này bằng cách xác định một từ đồng nghĩa loại đã lật các tham số kiểu xung quanh rồi sử dụng phần mở rộng TypeSynonymInstances, nhưng nó không thành công.

3

Bạn có thể sử dụng trình bao bọc newtype để hoán đổi thông số loại. Nhưng sau đó bạn nhận được một loại mới, và phải làm cho một sự phân biệt của bản gốc và loại bọc trong mã của bạn.

2

Câu trả lời câm mà bạn đã biết là: lật thông số của bạn!

Để GHC hỗ trợ loại điều này mà không cần thêm bất kỳ gói nào, bạn sẽ cần thứ gì đó như lambdas loại cấp, có thể sẽ không được thêm vào bất kỳ lúc nào. (Tôi rất muốn được chứng minh là sai về điều đó)

instance Functor (\a -> SomeType2 a m) where 
    -- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 b m 
    fmap f (SomeType2 lst m) = SomeType (map f lst) m 

Kể từ khi chúng tôi đã có TypeSynonymInstances, chúng ta có thể hy vọng cho PartiallyAppliedTypeSynonymInstances đôi khi hơi sớm hơn bao giờ.

type SomeType3 m a = SomeType2 a m 

instance Functor (SomeType m) where 
    -- fmap :: (a -> b) -> SomeType3 m a -> SomeType3 m b 
    -- or, synonymously: 
    -- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 a m 
    fmap f (SomeType2 lst m) = SomeType (map f lst) m 
+1

Giả sử bạn có PartiallyAppliedTypeSynonymInstances và xác định 'type T a = Integer'. Sau đó, chúng ta sẽ có thể nói 'instance Functor T trong đó fmap _ x = x'. Vì vậy, rất khó để nói rõ ràng là mã sai như 'fmap (+ 1) 7' không nên đánh dấu kiểm tra! (Ngay cả trong trường hợp bạn không xác định các trường hợp ngu ngốc, bạn đang làm suy luận kiểu tất cả các loại khó khăn) –

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