Подтвердить что ты не робот

Создание (a, a) Функтора

Как я могу сделать (a, a) a Functor, не прибегая к newtype?

В основном я хочу, чтобы он работал следующим образом:

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?

4b9b3361

Ответ 1

Как заявили другие, нет никакого способа сделать это, не прибегая к новым типам или объявлениям данных. Однако посмотрели ли вы на Control.Arrow? Многие из этих функций очень полезны с кортежами, например:

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

Ответ 2

Вы можете объявить

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

Однако это не ограничивает первый элемент вашей пары, а fmap будет действовать только на второй элемент.

Проблема заключается в том, что кортеж не обеспечивает связь между типами двух элементов.

Если вам не нужен декоратор newtype, вы можете создать свой собственный новый тип:

data Pair a = P a a

instance Functor Pair where
  ...

с которым будет легче работать, чем newtype вокруг кортежа.

Ответ 3

С singletons вы можете определить класс типа Functor для defunctionalized символов (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)