考虑以下类型:
data SomeType m a = SomeType (m Integer) [a]
我们可以使用以下代码轻松地将该类型作为Functor的实例:
instance Functor (SomeType m) where
fmap f (SomeType m lst) = SomeType m (map f lst)
但是,如果交换了SomeType
类型的参数:
data SomeType2 a m = SomeType2 (m Integer) [a]
然后上面的实例定义不起作用。
有没有办法让SomeType2
成为Functor的一个实例?如果没有,那么haskell / ghc是否有任何新增功能可以实现?
答案 0 :(得分:20)
我有偏见,但我认为这是一个很好的机会,可以使用Control.Newtype,这是一个小小的工具包,只需要安装新版本"程。
这是这笔交易。您想要翻转类型构造函数,以便在不同的参数中获得有趣的(例如)。定义新类型
newtype Flip f x y = Flip (f y x)
并将其添加到Newtype
类
instance Newtype (Flip f x y) (f y x) where
pack = Flip
unpack (Flip z) = z
Newtype
类只是一个将newtypes映射到它们未经修饰的等价物的目录,提供了方便的套件,例如: op Flip
与Flip
相反:您不需要记住您所谓的内容。
对于有问题的问题,我们现在可以做这样的事情:
data Bif x y = BNil | BCons x y (Bif x y) deriving Show
这是一个两参数数据类型,恰好在两个参数中都是函数式的。 (或许,我们应该把它作为Bifunctor类的一个实例,但无论如何......)我们可以使它成为Functor
两次:一次用于最后一个参数......
instance Functor (Bif x) where
fmap f BNil = BNil
fmap f (BCons x y b) = BCons x (f y) (fmap f b)
......一次是第一次:
instance Functor (Flip Bif y) where
fmap f (Flip BNil) = Flip BNil
fmap f (Flip (BCons x y b)) = Flip (BCons (f x) y (under Flip (fmap f) b))
其中under p f
是一种简洁的方式来说op p . f . p
。
我告诉你没有谎言:让我们试试。
someBif :: Bif Int Char
someBif = BCons 1 'a' (BCons 2 'b' (BCons 3 'c' BNil))
然后我们得到
*Flip> fmap succ someBif
BCons 1 'b' (BCons 2 'c' (BCons 3 'd' BNil))
*Flip> under Flip (fmap succ) someBif
BCons 2 'a' (BCons 3 'b' (BCons 4 'c' BNil))
在这种情况下,有很多方法可以将同一件事看作是Functor
,所以我们必须发出一些声音来说明我们的意思。但如果你对它有系统的话,噪音并不是那么大。
答案 1 :(得分:3)
这是不可能的,我认为不会很快。
类型参数的顺序非常重要。最后一个值是您要“包含”的值,以便与Functor等一起使用
我尝试通过定义一个类型同义词来解决这个问题,该类型同义词翻转了类型参数,然后使用TypeSynonymInstances
扩展名,但它无法正常工作。
答案 2 :(得分:3)
您可以使用交换类型参数的newtype
包装器。但是你得到一个新类型,并且必须区分代码中的原始类型和包装类型。
答案 3 :(得分:2)
你已经知道的愚蠢答案是:翻转你的参数!
对于GHC来说,在没有任何额外包装的情况下支持这种事情,你需要像Type-level lambdas这样的东西,它们很可能不会很快被添加。 (我很乐意被证明是错的)
instance Functor (\a -> SomeType2 a m) where
-- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 b m
fmap f (SomeType2 lst m) = SomeType (map f lst) m
由于我们已经有了TypeSynonymInstances,我们可能希望PartiallyAppliedTypeSynonymInstances有时会比从未稍微好一点。
type SomeType3 m a = SomeType2 a m
instance Functor (SomeType m) where
-- fmap :: (a -> b) -> SomeType3 m a -> SomeType3 m b
-- or, synonymously:
-- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 a m
fmap f (SomeType2 lst m) = SomeType (map f lst) m