组成两个函子是什么意思?

What does it mean to compose two Functors?

提问人:akbiggs 提问时间:11/5/2013 最后编辑:recursion.ninjaakbiggs 更新时间:8/28/2021 访问量:5390

问:

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

两个函子的组成也是一个函子。

起初我以为这是在谈论由两个单独的实例定义的方法,但这并没有真正的意义,因为据我所知,这些类型不会匹配。对于两种类型 和 ,的类型将是 和 ,这似乎不是真正可组合的。那么组成两个意味着什么呢?fmapFunctorff'fmapfmap :: (a -> b) -> f a -> f bfmap :: (a -> b) -> f' a -> f' bFunctors

Haskell 理论 函子

评论

3赞 Squidly 11/5/2013
你有没有试过在ghci中实际编写fmap?即:t fmap . fmap
2赞 akbiggs 11/6/2013
@MrBones 感谢您的提示!对于那些没有 ghci 访问权限的人,输出是:: (Functor f1, Functor f) => (a -> b) -> f (f1 a) -> f (f1 b)

答:

31赞 Daniel Wagner 11/5/2013 #1

A 给出了两个映射:一个是类型级将类型映射到类型(这是 in ),另一个是计算级将函数映射到函数(这是 in )。您正在考虑编写计算级映射,但应该考虑编写类型级映射;例如,给定Functorxinstance Functor x wherexfmap = x

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

你能写吗

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

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

评论

3赞 Will 12/1/2015
这是一个很好的答案,它解释了 Typeclassopedia 练习,而不会泄露解决方案。
13赞 kqr 11/5/2013 #2

两个函数的组成是将一个函数放在另一个函数中,例如

round (sqrt 23)

这是两个函数和 的组成。同样,两个函子的组成是将一个函子放在另一个函子中,例如roundsqrt

Just [3, 5, 6, 2]

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

14赞 Stephen Diehl 11/5/2013 #3

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

对于第二个函子,函子的组合只是将变换的共域作为变换的域。在Haskell中,这是通过一些新类型的解包来实现的。G : D -> EG . F : C -> EFfmapGfmap

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

评论

1赞 Kamel 8/30/2015
是否同时指和?如果答案是肯定的,那么为什么 not 的类型约束不适用于 ?ffunctorfunctionFunctor fffmap f
19赞 Luis Casillas 11/6/2013 #4

这里谈论的是类型构造函数的组合,如 和 ,而不是函数的组合。因此,例如,有两种作曲方式和:[]Maybefmap[]Maybe

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

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

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)

Compose对扩展特别有用:GeneralizedNewtypeDeriving

{-# 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)

请注意,两个 s 的组成也是一个 .因此,既然 和 是 s,那么 和 也是如此。作曲是一种非常简洁的技术,如今正慢慢变得越来越普遍,作为单子转换器的替代品,适用于不需要单子的全部功能的情况。ApplicativeApplicative[]MaybeApplicativeCompose [] MaybeListOfMaybeApplicative

评论

2赞 Marco Faustinelli 3/16/2015
当然,这里所有答案中最有用的。让我进一步问你:当你写fmap f(Compose x)时,x的类型是什么?我会说它是 f g a,但我仍然难以可视化计算 (fmap (fmap f) x)。必须更加努力地考虑getCompose。注意:我知道字母“f”在这里用于两个不同的角色。
1赞 amalloy 4/23/2019
@MarcoFaustinelli 您可以通过查看要匹配的 Compose 构造函数来判断它是什么类型。因为 ,当你对它进行模式匹配时,你会得到一个 类型的值。newtype Compose f g a = Compose (f (g a))f (g a)
1赞 Will Ness 8/28/2021
@MarcoFaustinelli如果它被定义为 ,那就是 。newtype Compose f g a = MkCompose (f (g a))fmap foo (MkCompose x) = MkCompose (fmap (fmap foo) x)