2011-01-27 45 views
19

Tôi làm cách nào để tạo (a, a) một Functor mà không cần sử dụng số newtype?Làm (a, a) a Functor

Về cơ bản tôi muốn nó hoạt động như thế này:

instance Functor (a, a) where 
    fmap f (x, y) = (f x, f y) 

Nhưng tất nhiên đó không phải là một cách hợp pháp để thể hiện nó:

Kind mis-match 
The first argument of `Functor' should have kind `* -> *', 
but `(a, a)' has kind `*' 
In the instance declaration for `Functor (a, a)' 

Những gì tôi thực sự muốn là một chức năng loại cấp như điều này: \a -> (a, a) (cú pháp không hợp lệ). Vì vậy, một bí danh loại, có lẽ?

type V2 a = (a, a) 
instance Functor V2 where 
    fmap f (x, y) = (f x, f y) 

Tôi nghĩ điều này sẽ hiệu quả nhưng không. Đầu tiên tôi nhận được đơn khiếu nại này:

Illegal instance declaration for `Functor V2' 
(All instance types must be of the form (T t1 ... tn) 
where T is not a synonym. 
Use -XTypeSynonymInstances if you want to disable this.) 
In the instance declaration for `Functor V2' 

Nếu tôi làm theo những lời khuyên và thêm phần mở rộng TypeSynonymInstances, tôi nhận được một lỗi mới:

Type synonym `V2' should have 1 argument, but has been given 0 
In the instance declaration for `Functor V2' 

Vâng, duh, đó là điểm! V2 có loại * -> * là yêu cầu bắt buộc đối với phiên bản Functor. Vâng, ok, tôi có thể sử dụng một newtype như thế này:

newtype V2 a = V2 (a, a) 
instance Functor V2 where 
    fmap f (V2 (x, y)) = V2 (f x, f y) 

Nhưng bây giờ tôi đã có rắc V2 s tự do khắp mã của tôi thay vì chỉ có khả năng đối phó với các bộ đơn giản, trong đó loại đánh bại điểm làm cho nó một Functor; tại thời điểm đó tôi cũng có thể làm cho chức năng của riêng tôi vmap :: (a -> b) -> (a, a) -> (b, b).

Vì vậy, có cách nào để làm điều này độc đáo, tức là không có newtype?

+0

Khi nào bạn muốn tạo bộ dữ liệu cho hàm Functor như thế này? Dường như với tôi rằng nếu bạn cần quyền hạn uber-Functor để hoạt động trên các bộ trường hợp đặc biệt, có lẽ bạn nên sử dụng một cấu trúc dữ liệu tùy chỉnh, chứ không phải là bộ dữ liệu, ngay từ đầu. Các bộ tuples bạn đang thao tác đại diện cho những gì? –

+4

@ Tôi có thể không _need_ "quyền hạn uber-Functor", nó sẽ chỉ nhẹ nhàng thuận tiện, có vẻ như nó sẽ là có thể, và nếu nó không phải là tôi tò mò tại sao. –

+0

@pelotom Tôi đồng ý rằng có vẻ như nó sẽ là có thể, mặc dù nó xuất hiện rằng nó không phải là. Tôi chỉ nghĩ rằng tôi sẽ mất một chút thời gian để có được trên soapbox của tôi và rao giảng sự tốt lành của việc làm cho một cấu trúc biểu cảm phù hợp với vấn đề của bạn, chứ không phải là quá tải tuples. –

Trả lời

15

Như những người khác đã nêu, không có cách nào để thực hiện việc này mà không cần sử dụng newtypes hoặc khai báo dữ liệu. Tuy nhiên, bạn đã xem số Control.Arrow chưa?Nhiều người trong số những chức năng này rất hữu ích với các bộ, ví dụ:

vmap :: (a -> b) -> (a,a) -> (b,b) 
vmap f = f *** f 
+0

Ý tưởng hay, cảm ơn! –

+1

Tốt hơn là 'vmap = join (***)' – alternative

4

Bạn có thể tuyên bố

instance Functor ((,) a) where 
    ... 

Tuy nhiên điều đó không có hạn chế các yếu tố đầu tiên của cặp của bạn, và fmap sẽ chỉ hành động dựa trên các yếu tố thứ hai.

Vấn đề là một bộ không thực thi mối quan hệ giữa các loại của hai yếu tố.

Nếu bạn không muốn có một trang trí newtype bạn có thể làm loại tươi của riêng bạn:

data Pair a = P a a 

instance Functor Pair where 
    ... 

đó sẽ dễ dàng hơn để làm việc với hơn một newtype xung quanh một tuple.

+1

Tôi không muốn một functor chỉ hoạt động trên một trong các thành phần của tuple, tôi muốn một functor cho '(a, a)', hoạt động trên cả hai phần tử đầu tiên và thứ hai (vì chúng có cùng kiểu) . Và tôi đang cố gắng tránh tạo một kiểu dữ liệu mới. –

+1

@pelotom, không thể. Functor nhận một đối số hàm tạo dữ liệu, '* -> *', và '(a, a)' không phải là một trong số đó. Bạn phải sử dụng 'newtype' hoặc' data'. – luqui

+0

@luqui Tôi giả sử bạn có nghĩa là họ yêu cầu một nhà xây dựng kiểu? Đó là về những gì tôi sợ ... nhưng tôi thấy không có lý do tại sao các loại bí danh không nên làm việc. –

0

Với singletons bạn có thể định nghĩa một lớp Functor loại cho các ký hiệu defunctionalized (Type ~> Type thay vì Type -> Type)

{-# Language ExplicitNamespaces, TypeApplications, TypeOperators, KindSignatures, ScopedTypeVariables, DataKinds, TypeInType, TypeFamilies, AllowAmbiguousTypes, InstanceSigs #-} 

import Data.Kind (Type) 
import Data.Singletons (type (~>), Apply) 

class Functor' (f :: Type ~> Type) where 
    fmap' :: (a -> a') -> (Apply f a -> Apply f a') 

data Dup :: Type ~> Type 

type instance Dup `Apply` a = (a, a) 

instance Functor' Dup where 
    fmap' :: (a -> a') -> ((a, a) -> (a', a')) 
    fmap' f (a1, a2) = (f a1, f a2) 

này mang đến cho bạn một Prelude.Functor ví dụ tự động

newtype f $ a = App (Apply f a) 

instance Functor' f => Functor (($) f) where 
    fmap :: (a -> a') -> (f $ a -> f $ a') 
    fmap f (App fa) = App (fmap' @f f fa) 
Các vấn đề liên quan