2011-01-27 35 views
19

如何製作(a, a) a Functor而不訴諸於newtype製作(a,a)分散器

基本上我希望它是這樣工作的:

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

當然,這並不表示它一個合法的方式,而是:

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

我真正想要的是像一個類型級功能這個:\a -> (a, a)(語法無效)。那麼可能是一個類型別名?

type V2 a = (a, a) 
instance Functor V2 where 
    fmap f (x, y) = (f x, f 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' 

如果我照做,並添加TypeSynonymInstances擴展,我得到一個新的錯誤:

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

嗯,真不錯,這就是問題所在! V2有種* -> *,這是Functor實例所要求的。好吧,好吧,我可以用一個newtype這樣的:

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

但現在我得撒V2小號寬鬆在我的代碼,而不是隻能夠處理簡單的元組,哪一種違背了點使其成爲Functor;在那一點上,我不妨做我自己的功能vmap :: (a -> b) -> (a, a) -> (b, b)

那麼有沒有什麼辦法可以做到這一點,即沒有newtype

+0

什麼時候你想讓元組變成這樣的Functor?在我看來,如果你需要uber-Functor的權力來操作特殊的元組,你應該首先使用自定義的數據結構,而不是元組。你操縱的元組代表什麼? – 2011-01-27 05:17:55

+4

@丹我並不需要「超級強大的功能」,它只是輕度方便,似乎應該是可能的,如果不是我很好奇爲什麼。 – 2011-01-27 05:34:47

+0

@pelotom我同意這看起來應該是可能的,儘管看起來不是這樣。我只是想,我會花一點時間來使用我的soapbox,並宣傳爲你的問題量身定製一個表達式結構的好處,而不是重載元組。 – 2011-01-27 05:42:02

回答

15

正如其他人所說,沒有訴諸新類型或數據聲明,就沒有辦法做到這一點。但是,你看過Control.Arrow?其中許多功能與元組非常有用,例如:

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

你可以聲明

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

但是這並不限制你對的第一個元素,fmap只會在第二元件上採取行動。

問題是元組不強制兩個元素的類型之間的關係。

如果你不想newtype裝飾,你可以讓自己的清爽型:

data Pair a = P a a 

instance Functor Pair where 
    ... 

,這將是比單純的圍繞一個元組newtype工作。

0

隨着singletons可以定義去官能化​​的符號Functor類型的類(的Type ~> Type代替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) 

這給你一個Prelude.Functor自動實例

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)