制作(a,a)一个Functor

时间:2011-01-27 04:06:58

标签: haskell types functor typeclass

如何在不诉诸(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

3 个答案:

答案 0 :(得分:16)

正如其他人所说,如果不诉诸新类型或数据声明,就没有办法做到这一点。但是,你看过Control.Arrow了吗?其中许多函数对于元组非常有用,例如:

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

答案 1 :(得分:4)

您可以声明

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

但是,这并不会限制你对的第一个元素,而fmap只会对第二个元素起作用。

问题是元组不会强制两个元素的类型之间存在关系。

如果你不想要一个newtype装饰者,你可以制作自己的新鲜类型:

data Pair a = P a a

instance Functor Pair where
  ...

比元组周围的newtype更容易使用。

答案 2 :(得分:0)

使用singletons,您可以为功能化符号定义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)