在函数式编程中,什么是函子?

在阅读关于函数式编程的各种文章时,我几次遇到'函子'这个词,但作者通常假设读者已经理解这个术语。 环顾网络提供了过度的技术性描述(参见维基百科文章)或难以置信的模糊描述(请参阅此ocaml-tutorial网站上有关Functors的部分)。

有人可以善意地定义这个术语,解释它的用法,也许可以提供一个关于如何创建和使用仿函数的例子。

编辑 :虽然我对这个术语背后的理论很感兴趣,但我对这一理论的兴趣不如我在实施和实际使用这个概念。

编辑2 :看起来像是有一些跨代术语正在发生:我特指函数式编程的函子,而不是C ++的函数对象。


“函子”一词来自类别理论,这是一个非常普遍,非常抽象的数学分支。 功能语言的设计者至少以两种不同的方式借用它。

  • 在ML语言系列中,仿函数是一个将一个或多个其他模块作为参数的模块。 它被认为是一项高级功能,大多数初学程序员都遇到了困难。

    作为实现和实际应用的一个例子,你可以一劳永逸地定义你最喜欢的平衡二叉搜索树形式,它将作为参数提供一个模块,它提供:

  • 在二叉树中使用的键的类型

  • 按键上的总排序功能

  • 一旦你完成了这个任务,你可以永远使用相同的平衡二叉树实现。 (树中存储的值的类型通常是多态的 - 树不需要查看除复制它们之外的值,而树肯定需要能够比较键,并从中获取比较函数函子的参数。)

    ML仿函数的另一个应用是分层网络协议。 链接是由CMU福克斯集团撰写的一篇非常了不起的论文; 它展示了如何使用仿函数在更简单的图层类型(如IP或甚至直接通过以太网)上构建更复杂的协议层(如TCP)。 每个图层都是作为一个函子实现的,它将下面的图层作为参数。 软件的结构实际上反映了人们思考问题的方式,而不是程序员头脑中存在的层。 1994年这项工作发表时,这是一件大事。

    对于一个ML函子的实例,你可以看到ML Module Mania,它包含一个可发布函数的例子(即可怕的例子)。 有关ML模块系统(与其他类型模块进行比较)的精彩,清晰,透彻的解释,请阅读Xavier Leroy辉煌的1994 POPL纸张清单类型,模块和独立汇编的前几页。

  • 在Haskell和一些相关的纯函数式语言中, Functor是一个类型类。 一个类型属于一个类型类(或者更技术上讲,类型“是类型类的一个实例),当类型提供某些预期行为的某些操作时。 如果类型T具有某种类似集合的行为,则它可以属于类Functor

  • 类型T是通过另一种类型进行参数化的,您应该将其视为集合的元素类型。 如果您包含整数,字符串或布尔值,那么完整集合的类型就类似于T IntT StringT Bool 。 如果元素类型未知,则将其写为类型参数a ,如T a

    实例包括列表(类型的零个或多个元件a ),则Maybe类型(Type的零个或一个元件a ),套类型的元素的a ,类型的元件的阵列a ,各种包含类型的值搜索树的a ,还有很多其他你可以想到的。

  • T必须满足的另一个属性是,如果你有一个类型a -> b (对元素的函数)的函数,那么你必须能够使用该函数并在集合上产生一个相关函数。 您可以使用运算符fmap执行此操作,该运算符由Functor类型类中的每个类型共享。 运算符实际上是重载的,所以如果你有一个函数, even类型为Int -> Bool ,那么

    fmap even
    

    是一个重载函数,可以做许多美妙的事情:

  • 将整数列表转换为布尔值列表

  • 将整数树转换为布尔值树

  • Nothing转换为NothingJust 7转换为Just False

  • 在Haskell中,通过给出fmap的类型来表示该属性:

    fmap :: (Functor t) => (a -> b) -> t a -> t b
    

    我们现在有一个小的t ,这意味着“ Functor类中的任何类型”。

    长话短说,在Haskell中, 一个函数是一种集合,如果给它一个函数元素, fmap会给你一个集合函数 。 正如你可以想象的那样,这是一个可以广泛重用的思想,这就是为什么它作为Haskell标准库的一部分而受到祝福的原因。

    像往常一样,人们不断发明新的,有用的抽象概念,并且您可能需要研究应用函子,最好的参考文献可能是Conor McBride和Ross Paterson撰写的名为Applicative Programming with Effects的论文。


    这里的其他答案是完整的,但我会尝试使用函子的FP的另一种解释。 以此作为比喻:

    函子是一个类型为a的容器,当它受到从a→b映射的函数时,产生一个类型为b的容器。

    与C ++中抽象函数指针的使用不同,这里函数不是函数; 相反,当它受到函数的影响时,它的行为是一致的。


    有三种不同的含义,没有多大关系!

  • 在Ocaml中,它是一个参数化模块。 参见手册。 我认为最好的方法是举例:(写得很快,可能是越野车)

    module type Order = sig
        type t
        val compare: t -> t -> bool
    end;;
    
    
    module Integers = struct
        type t = int
        let compare x y = x > y
    end;;
    
    module ReverseOrder = functor (X: Order) -> struct
        type t = X.t
        let compare x y = X.compare y x
    end;;
    
    (* We can order reversely *)
    module K = ReverseOrder (Integers);;
    Integers.compare 3 4;;   (* this is false *)
    K.compare 3 4;;          (* this is true *)
    
    module LexicographicOrder = functor (X: Order) -> 
      functor (Y: Order) -> struct
        type t = X.t * Y.t
        let compare (a,b) (c,d) = if X.compare a c then true
                             else if X.compare c a then false
                             else Y.compare b d
    end;;
    
    (* compare lexicographically *)
    module X = LexicographicOrder (Integers) (Integers);;
    X.compare (2,3) (4,5);;
    
    module LinearSearch = functor (X: Order) -> struct
        type t = X.t array
        let find x k = 0 (* some boring code *)
    end;;
    
    module BinarySearch = functor (X: Order) -> struct
        type t = X.t array
        let find x k = 0 (* some boring code *)
    end;;
    
    (* linear search over arrays of integers *)
    module LS = LinearSearch (Integers);;
    LS.find [|1;2;3] 2;;
    (* binary search over arrays of pairs of integers, 
       sorted lexicographically *)
    module BS = BinarySearch (LexicographicOrder (Integers) (Integers));;
    BS.find [|(2,3);(4,5)|] (2,3);;
    
  • 您现在可以快速添加许多可能的订单,形成新订单的方式,轻松地在它们上执行二进制或线性搜索。 通用编程FTW。

  • 在像Haskell这样的函数式编程语言中,它意味着可以被“映射”的一些类型构造函数(参数化类型,如列表,集合)。 准确地说,一个仿函数f配备了(a -> b) -> (fa -> fb) 。 这源于类别理论。 您链接到的维基百科文章就是这种用法。

    class Functor f where
        fmap :: (a -> b) -> (f a -> f b)
    
    instance Functor [] where      -- lists are a functor
        fmap = map
    
    instance Functor Maybe where   -- Maybe is option in Haskell
        fmap f (Just x) = Just (f x)
        fmap f Nothing = Nothing
    
    fmap (+1) [2,3,4]   -- this is [3,4,5]
    fmap (+1) (Just 5)  -- this is Just 6
    fmap (+1) Nothing   -- this is Nothing
    
  • 所以,这是一种特殊的类型构造函数,并且与Ocaml中的函数无关!

  • 在命令式语言中,它是一个指向函数的指针。
  • 链接地址: http://www.djcxy.com/p/8369.html

    上一篇: In Functional Programming, what is a functor?

    下一篇: Efficiency of purely functional programming