Создание (а, а) функтора - PullRequest
19 голосов
/ 27 января 2011

Как мне сделать (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 s по всему коду, вместо того, чтобы просто иметь дело с простыми кортежами, что лишает смысла превращать его в Functor; в этот момент я мог бы также сделать свою собственную функцию vmap :: (a -> b) -> (a, a) -> (b, b).

Так есть ли способ сделать это красиво, то есть без newtype?

Ответы [ 3 ]

16 голосов
/ 27 января 2011

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

vmap :: (a -> b) -> (a,a) -> (b,b)
vmap f = f *** f
4 голосов
/ 27 января 2011

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

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

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

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

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

data Pair a = P a a

instance Functor Pair where
  ...

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

0 голосов
/ 08 декабря 2017

С синглетами вы можете определить класс типа Functor для нефункционализированных символов (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) 
...