首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么是双函子?

什么是双函子?
EN

Stack Overflow用户
提问于 2016-12-10 09:03:58
回答 1查看 3.9K关注 0票数 14

我对Haskell比较陌生,很难理解双函子的效用。我想我从理论上理解了它们:例如,如果我想要映射一个抽象多个具体类型的类型,例如,或者可能,我需要将它们封装在一个双函子中。但一方面,这些例子似乎是特别人为的,另一方面,似乎你可以通过简单的合成来实现同样的功能。

例如,我在https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf中看到了由Jeremy和BrunoC.D.S.Oliveira编写的代码:

代码语言:javascript
复制
import Data.Bifunctor

data Fix s a = In {out::s a (Fix s a) }

map' :: Bifunctor s => (a -> b) -> Fix s a -> Fix s b
map' f = In . bimap f (map' f) . out

fold' :: Bifunctor s => (s a b -> b) -> Fix s a -> b
fold' f = f . bimap id (fold' f) . out

unfold' :: Bifunctor s => (b -> s a b) -> b -> Fix s a
unfold' f = In . bimap id (unfold' f) . f

我理解其要点是组合映射和折叠函数来创建迭代器模式,这是通过定义一个需要两个参数的数据构造函数来实现的。但在实践中,我不明白这与使用常规函子和使用fmap而不是bimap组合函数有什么不同。我想我显然遗漏了一些东西,不管是这个例子,还是一般情况下。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-12-10 12:55:27

我觉得你想得有点过头了。双函子就像一个双参数函子。Gibbons和Oliveira的想法只是双函子的一个应用,就像递归格式的标准动物园只是函子的一个应用一样。

代码语言:javascript
复制
class Bifunctor f where
    bimap :: (a -> c) -> (b -> d) -> f a b -> f c d

Bifunctor具有一种* -> * -> *,这两个参数都可以进行协变量映射。将其与常规Functor进行比较,后者只有一个参数(f :: * -> *),可以相互映射。

例如,想想Either常用的Functor实例,它只允许通过第二个类型的参数fmap -- Right值被映射,Left值保持原样。

代码语言:javascript
复制
instance Functor (Either a) where
    fmap f (Left x) = Left x
    fmap f (Right y) = Right (f y)

但是,它的Bifunctor实例允许您映射之和的两部分。

代码语言:javascript
复制
instance Bifunctor Either where
    bimap f g (Left x) = Left (f x)
    bimap f g (Right y) = Right (g y)

对于元组也是这样:(,)Functor实例只允许映射第二个组件,而Bifunctor允许映射这两个组件。

代码语言:javascript
复制
instance Functor ((,) a) where
    fmap f (x, y) = (x, f y)

instance Bifunctor (,) where
    bimap f g (x, y) = (f x, g y)

请注意,您提到的Maybe不适合于双函子框架,因为它只有一个参数。

关于Fix问题,双函子的不动点允许您描述具有函数类型参数的递归类型,例如大多数类似容器的结构。让我们以列表为例。

代码语言:javascript
复制
newtype Fix f = Fix { unFix :: f (Fix f) }

data ListF a r = Nil_ | Cons_ a r deriving Functor
type List a = Fix (ListF a)

像上面一样,使用标准函数式Fix,没有Functor for List实例的泛型派生,因为FixLista参数一无所知,也就是说,我不能编写类似instance Something f => Functor (Fix f)的任何东西,因为Fix有错误的类型。我必须为列表添加一个map,可能会使用cata >。

代码语言:javascript
复制
map :: (a -> b) -> List a -> List b
map f = cata phi
    where phi Nil_ = Fix Nil_
          phi Cons_ x r = Fix $ Cons_ (f x) r

Fix的双功能版本确实允许一个Functor实例。Fix使用双函子的一个参数插入Fix f a的递归出现,另一个参数作为结果数据类型的函数参数。

代码语言:javascript
复制
newtype Fix f a = Fix { unFix :: f a (Fix f a) }

instance Bifunctor f => Functor (Fix f) where
    fmap f = Fix . bimap f (fmap f) . unFix

这样我们就可以写:

代码语言:javascript
复制
deriveBifunctor ''ListF

type List = Fix ListF

并免费获取Functor实例:

代码语言:javascript
复制
map :: (a -> b) -> List a -> List b
map = fmap

当然,如果您想泛化使用具有多个参数的递归结构,则需要泛化为三函子、四函子等.这显然是不可持续的,大量的工作(用更高级的编程语言)已经投入到设计更灵活的系统来描述类型。

票数 21
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41073862

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档