2011-01-27 12 views
19

Jak mogę wykonać (a, a) a Functor bez odwoływania się do ?Tworzenie (a, a) Functor

Zasadniczo chcę go do pracy tak:

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

Ale oczywiście nie jest to legalny sposób, aby to wyrazić:

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

Co naprawdę chcę to funkcja typu poziom jak to: \a -> (a, a) (nieprawidłowa składnia). A może typowy alias?

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

Myślę, że to zadziała, ale tak nie jest. Po pierwsze mam tego zarzutu

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' 

Jeśli śledzę informacje i dodaj rozszerzenie TypeSynonymInstances, pojawia się nowy błąd:

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

Dobrze, duh, że o to chodzi! V2 ma rodzaj * -> *, co jest wymagane od instancji Functor. No, ok, mogę użyć newtype takiego:

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

Ale teraz muszę posypać V2 s obficie w całym moim kodu zamiast po prostu będąc w stanie radzić sobie z prostymi krotek, które niby pokonuje punkt uczynienia tego Functor; w tym momencie mogę równie dobrze spełniać swoją funkcję vmap :: (a -> b) -> (a, a) -> (b, b).

Czy istnieje sposób, aby to zrobić ładnie, tj. Bez numeru newtype?

+0

Kiedy chciałbyś, aby krotki były tak funkcjonalne? Wydaje mi się, że jeśli potrzebujesz mocy uber-Functor do operowania na krotkach specjalnych, powinieneś przede wszystkim użyć niestandardowej struktury danych, a nie krotek. Co reprezentują krotki, którymi manipulujesz? –

+4

@Dan Nie _need_ "uber-Functor powers", to byłby umiarkowanie wygodny, wydawało się, że powinno być możliwe, a jeśli nie jest, jestem ciekawy dlaczego. –

+0

@pelotom Zgadzam się, że wydaje się, że powinno być możliwe, choć wydaje się, że tak nie jest. Po prostu pomyślałem, że poświęcę chwilę, by sięgnąć po moją mydelniczkę i głosić dobroć tworzenia ekspresyjnej struktury dostosowanej do twojego problemu, zamiast przeciążać krotki. –

Odpowiedz

15

Jak stwierdzili inni, nie można tego zrobić bez odwoływania się do nowych typów lub deklaracji danych. Jednak, czy spojrzałeś na Control.Arrow?Wiele z tych funkcji są bardzo użyteczne z krotek, na przykład:

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

Dobry pomysł, dzięki! –

+1

Lepsze jest 'vmap = join (***)' – alternative

4

Można zadeklarować

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

jednak, że nie ogranicza to pierwszy element swojej pary i fmap będzie działać tylko na drugim elemencie.

Problem polega na tym, że krotka nie wymusza relacji między typami dwóch elementów.

Jeśli nie chcesz newtype dekorator można zrobić własne świeżego typ:

data Pair a = P a a 

instance Functor Pair where 
    ... 

który będzie łatwiej pracować niż newtype wokół krotki.

+1

Nie chcę funktora, który działa tylko na jeden z elementów krotki, chcę funktora dla '(a, a)', który działa na pierwszym i drugim elemencie (ponieważ mają ten sam typ) . Próbuję uniknąć tworzenia nowego typu danych. –

+1

@pelotom, niemożliwe. Funktor przyjmuje argument konstruktora danych, '* -> *', a '(a, a)' nie jest jednym z nich. Musisz użyć 'newtype' lub' data'. – luqui

+0

@luqui Zakładam, że masz na myśli, że wymagają one konstruktora typu? Chodzi o to, czego się obawiałem ... ale nie widzę powodu, dla którego alias typu nie powinien działać. –

0

Z singletons można zdefiniować klasę Functor typu dla odfunkcyjniony symboli (Type ~> Type zamiast 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) 

To daje Prelude.Functor instancja automatycznie

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)