Создание (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
?
Ответы
Ответ 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)