如何在不诉诸(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
?
答案 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)