2011-01-27 14 views
19

¿Cómo puedo hacer (a, a) a Functor sin recurrir a un newtype?Fabricación (a, a) a Functor

Básicamente quiero que funcione de esta manera:

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

Pero por supuesto que no es una forma legal de expresarlo:

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

Lo que realmente quiero es una función de nivel tipo como esto: \a -> (a, a) (sintaxis inválida). Entonces, ¿un tipo de alias, tal vez?

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

Creo que esto funcionaría, pero no es así. En primer lugar consigo esta queja:

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' 

Si sigo el consejo y añadir la extensión TypeSynonymInstances, aparece un nuevo error:

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

Bueno, duh, ese es el punto! V2 tiene kind * -> * que es lo que se requiere de una instancia Functor. Pues bien, puedo utilizar un newtype así:

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

Pero ahora tengo que espolvorear V2 s liberalmente a través de mi código en lugar de sólo ser capaz de hacer frente a las tuplas simples, que tipo de derrotas el punto de hacerlo un Functor; en ese punto, también podría hacer mi propia función vmap :: (a -> b) -> (a, a) -> (b, b).

¿Hay alguna forma de hacerlo bien, es decir, sin newtype?

+0

¿Cuándo le gustaría hacer tuplas a Functor como este? Me parece que, si necesita poderes de súper Funcionador para operar en tuplas de casos especiales, en primer lugar debería usar una estructura de datos personalizada, en lugar de tuplas. ¿Qué representan las tuplas que estás manipulando? –

+4

@Dan no necesito _ "poderes de uber-Functor", hubiera sido ligeramente conveniente, parecía que debería ser posible, y si no es así, tengo curiosidad por saber por qué. –

+0

@pelotom Estoy de acuerdo en que parece que debería ser posible, aunque parece que no lo es. Solo pensé que me tomaría un momento para subirme a mi tribuna y predicar la bondad de hacer una estructura expresiva adaptada a su problema, en lugar de sobrecargar las tuplas. –

Respuesta

15

Como han indicado otros, no hay forma de hacerlo sin recurrir a nuevos tipos o declaraciones de datos. Sin embargo, ¿has mirado Control.Arrow?Muchas de estas funciones son muy útiles con tuplas, por ejemplo:

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

¡Buena idea, gracias! –

+1

Mejor es 'vmap = join (***)' – alternative

4

Se puede declarar

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

Sin embargo que no limite el primer elemento de su par, y fmap sólo sería actuar sobre el segundo elemento.

El problema es que una tupla no impone una relación entre los tipos de los dos elementos.

Si no desea que un decorador newtype usted puede hacer su propio tipo fresco:

data Pair a = P a a 

instance Functor Pair where 
    ... 

que será más fácil de trabajar que una newtype alrededor de una tupla.

+1

No quiero un functor que solo actúe sobre uno de los elementos de la tupla, quiero un funtor para '(a, a)', que actúa tanto en el primero como en el segundo elemento (porque tienen el mismo tipo) . Y estoy tratando de evitar crear un nuevo tipo de datos. –

+1

@pelotom, no es posible. Functor toma un argumento constructor de datos, '* -> *' y '(a, a)' no es uno de esos. Tienes que usar un 'newtype' o' data'. – luqui

+0

@luqui Supongo que quiere decir que requieren un constructor de tipo? Eso es lo que temía ... pero no veo ninguna razón por la cual el alias tipo no debería funcionar. –

0

Con singletons puede definir una clase Functor tipo de símbolos desfuncionalizadas (Type ~> Type en lugar de 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) 

Esto le da una Prelude.Functor instancia automáticamente

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) 
Cuestiones relacionadas