29

Haskell Typeclassopedia 第 3.2 节的练习 5要求对陈述进行证明或反例

两个 Functor 的组合也是一个 Functor。

起初我以为这是在谈论组合fmap由 a 的两个单独实例定义的方法Functor,但这并没有真正的意义,因为据我所知,类型不会匹配。对于两种类型fand f',类型 offmap将是fmap :: (a -> b) -> f a -> f band fmap :: (a -> b) -> f' a -> f' b,这看起来并不真正可组合。那么组合两个是什么意思Functors呢?

4

4 回答 4

30

AFunctor给出了两种映射:一种在类型级别将类型映射到类型(这是xin instance Functor x where),另一种是在计算级别将函数映射到函数(这是xin fmap = x)。您正在考虑编写计算级映射,但应该考虑编写类型级映射;例如,给定

newtype Compose f g x = Compose (f (g x))

你能写吗

instance (Functor f, Functor g) => Functor (Compose f g)

? 如果不是,为什么不呢?

于 2013-11-04T18:35:02.067 回答
19

这里所说的是类型构造函数的组合,例如[]and Maybe,而不是函数的组合,例如fmap。例如,有两种组合方式 []Maybe

newtype ListOfMabye a = ListOfMaybe [Maybe a]
newtype MaybeOfList a = MaybeOfList (Maybe [a])

两个的组合Functors是一个的声明Functor意味着有一种Functor为这些类型编写实例的公式化方式:

instance Functor ListOfMaybe where
    fmap f (ListOfMaybe x) = ListOfMaybe (fmap (fmap f) x) 

instance Functor MaybeOfList where
    fmap f (MaybeOfList x) = MaybeOfList (fmap (fmap f) x)

事实上,Haskell 平台附带的模块Data.Functor.Compose为您提供了Compose一种“免费”执行此操作的类型:

import Data.Functor.Compose

newtype Compose f g a = Compose { getCompose :: f (g a) }

instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose x) = Compose (fmap (fmap f) x)

ComposeGeneralizedNewtypeDeriving扩展特别有用:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype ListOfMaybe a = ListOfMaybe (Compose [] Maybe a)
   -- Now we can derive Functor and Applicative instances based on those of Compose
   deriving (Functor, Applicative)

请注意,两个Applicatives 的组合也是一个Applicative. 因此,因为[]MaybeApplicatives,所以Compose [] MaybeListOfMaybe。组合Applicatives 是一种非常巧妙的技术,如今它正在慢慢变得越来越普遍,作为 monad 转换器的替代方案,用于在不需要 monad 的全部功能的情况下。

于 2013-11-05T23:39:08.670 回答
14

两个函数的组合是当你将一个函数放在另一个函数中时,例如

round (sqrt 23)

这是两个函数round和的组合sqrt。类似地,两个函子的组合是当您将一个函子放在另一个函子中时,例如

Just [3, 5, 6, 2]

List 是一个函子,Maybe 也是。如果您尝试弄清楚 fmap 应该对上述值做什么,您可以对为什么它们的组合也是函子有一些直觉。当然它应该映射到内部函子的内容!

于 2013-11-04T18:42:29.690 回答
14

在这里考虑分类解释确实很有帮助,仿函数F: C -> D将对象(值)和态射(函数)带到对象,将态射从一个类别C带到对象和一个类别中的态射D

对于第二个函子G : D -> E,函子的组合G . F : C -> E只是将F fmap变换的共域作为变换的域G fmap。在 Haskell 中,这是通过一点新类型展开来完成的。

import Data.Functor

newtype Comp f g a = Comp { unComp :: f (g a) }

compose :: f (g a) -> Comp f g a
compose = Comp

decompose :: Comp f g a -> f (g a)
decompose = unComp

instance (Functor f, Functor g) => Functor (Comp f g) where
  fmap foo = compose . fmap (fmap foo) . decompose
于 2013-11-04T18:42:51.230 回答