monoid functors fmap haskell types functor typeclass

haskell - functors - Haciendo(a, a) un Functor



functor haskell (3)

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

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

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

Básicamente quiero que funcione así:

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

Pero, por supuesto, esa 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 de tipo como esta: /a -> (a, a) (sintaxis no válida). Entonces, ¿un alias de tipo, tal vez?

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

Pensaría que esto funcionaría, pero no funciona. Primero recibo 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 los consejos y agrego la extensión TypeSynonymInstances , recibo 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 el tipo * -> * que es lo que se requiere de una instancia de Functor . Bueno, ok, puedo usar un newtype como este:

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

Pero ahora tengo que esparcir generosamente V2 largo de mi código, en lugar de poder lidiar con tuplas simples, lo que derrota el punto de convertirlo en un Functor ; en ese punto yo también podría hacer mi propia función vmap :: (a -> b) -> (a, a) -> (b, b) .

Entonces, ¿hay alguna manera de hacerlo bien, es decir, sin un newtype ?


Con singletons puede definir una clase de tipo de Functor para símbolos desfuncionalizados ( Type ~> Type 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 te da una instancia de Prelude.Functor 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)


Puedes declarar

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

Sin embargo, eso no restringe el primer elemento de su par, y fmap solo actuará en el segundo elemento.

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

Si no quieres un decorador newtype puedes hacer tu propio tipo fresco:

data Pair a = P a a instance Functor Pair where ...

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