関数型プログラミングでは、ファンクタとは何ですか?


224

関数型プログラミングに関するさまざまな記事を読んでいる間、「Functor」という用語に何度か遭遇しましたが、著者は通常、読者がこの用語をすでに理解していると想定しています。Webを見回すと、過度に技術的な説明(Wikipediaの記事を参照)または信じられないほどあいまいな説明(このocamlチュートリアルWebサイトの Functorsのセクションを参照)が提供されています。

誰かが親切に用語を定義し、その使用法を説明し、おそらくファンクターがどのように作成され使用されるかの例を提供できますか?

編集:私はこの用語の背後にある理論に興味がありますが、その概念の実装と実際の使用には興味がありません。

編集2:いくつかのクロスターミノリジーが起こっているように見えます:私は特にC ++の関数オブジェクトではなく、関数型プログラミングのファンクターに言及しています。


4
次も参照してください:adit.io/posts/…–
Vlad the Impala、

かなり良い答えも:stackoverflow.com/a/45149475/1498178
toraritte

概念の背後にある成層圏の用語や理論よりも実用的な実装と使用法に関心がある場合は、1つのライナーが必要です。ファンクターは「マップ」関数を公開します。
Richard Gomes

@RichardGomes IMHOファンクターの役割を単純なJavaのようなインターフェースに減らしますが、そうではありません。functorはものを変換し、(Haskellの)既存の型から新しい型を構築します。つまり、型もマップされます。fmap関数をマップします。関係するマッピングには2種類あります。物事をそのように見る方法は、カテゴリー理論(より一般的です)を理解するのに役立ちます。つまり、基本的なカテゴリー理論を理解して、Haskellのすべてのカテゴリー理論(ファンクター、モナド、...)を支援することは興味深いことです。
Ludovic Kuty

@VladtheImpalaブログ投稿は素晴らしいですが、たとえそれが大いに役立ったとしても、ファンクターが別のタイプを構築(マップ)することを覚えておきたいです。モナドの「ファンクターFが各タイプTを受け取り、それを新しいタイプFTにマップする」という文は特にブリトーのようです。私見それがこのようなものを見るのに実用的であると証明されたとしても、それは単なる値の前後関係(ボックス)ではありません(Haskell PoV対カテゴリー理論PoV?)
Ludovic Kuty

回答:


273

「ファンクター」という言葉は、数学の非常に一般的で非常に抽象的な分野であるカテゴリー理論に由来します。関数型言語の設計者は、少なくとも2つの異なる方法でそれを借りてきました。

  • 言語のMLファミリーでは、ファンクターは1つ以上の他のモジュールをパラメーターとして受け取るモジュールです。これは高度な機能と見なされており、ほとんどの初心者プログラマーはこの機能に問題を抱えています。

    実装と実際の使用の例として、バランスのとれたバイナリ検索ツリーのお気に入りのフォームをファンクタとして定義すると、次のものを提供するモジュールがパラメータとして使用されます。

    • バイナリツリーで使用されるキーのタイプ

    • キーの全順序関数

    これを実行したら、同じバランスのとれたバイナリツリーの実装を永久に使用できます。(通常、ツリーに格納される値のタイプは多相のままです。ツリーは値をコピーする以外に値を調べる必要はありませんが、ツリーはキーを確実に比較できる必要があり、比較関数を取得します。ファンクターのパラメーター。)

    MLファンクタのもう1つのアプリケーションは、階層型ネットワークプロトコルです。リンクは、CMU Foxグループによる本当に素晴らしい論文へのリンクです。ファンクターを使用して、より複雑なプロトコルレイヤー(TCPなど)をより単純なレイヤーのタイプ(IPなど、またはイーサネット経由で直接)に構築する方法を示します。各レイヤーは、その下のレイヤーをパラメーターとして受け取るファンクターとして実装されます。ソフトウェアの構造は、プログラマーの頭にだけ存在する層とは対照的に、実際の問題に対する考え方を反映しています。この作品が出版された1994年に、それは大したことでした。

    実際のMLファンクタのワイルドな例については、紙のML Module Maniaを参照してください。これには、動作中のファンクタの公開可能な(つまり、恐ろしい)サンプルが含まれています。MLモジュールシステム(他の種類のモジュールとの比較)の鮮明で明確な説明については、Xavier Leroyの優れた1994 POPL論文のManifest Types、Modules、and Separate Compilationの最初の数ページをお読みください。

  • Haskellおよび関連する純粋な関数言語でFunctorは、型クラスです。型が特定の操作で特定の予想される動作を提供する場合、型は型クラス(より厳密には、型は型クラスの「インスタンスである」)に属します。特定のコレクションのような動作がある場合、型Tはクラスに属することができFunctorます。

    • Tは別の型にパラメーター化されます。これは、コレクションの要素型と考える必要があります。フルコレクションのタイプは、その後のようなものであるT IntT StringT Bool、あなたはそれぞれ整数、文字列、またはブール値を含むされている場合。要素のタイプが不明な場合、のようにタイプパラメータ として記述aされT aます。

      例には、リスト(タイプのゼロ以上の要素a)、Maybeタイプ(タイプのゼロまたは1つの要素a)、タイプの要素のセット、タイプaの要素の配列、タイプのa値を含むすべての種類の検索ツリーa、およびその他の多くの考えることができます。

    • T満たす必要があるもう1つのプロパティは、タイプa -> bの関数(要素の関数)がある場合、その関数を取得して、コレクションの関連関数を生成できる必要があることです。これを行うにfmapは、Functor型クラスのすべての型で共有される演算子を使用します。演算子は実際にはオーバーロードされているためeven、typeの関数がある場合Int -> Bool

      fmap even

      多くの素晴らしいことを行うことができるオーバーロードされた関数です:

      • 整数のリストをブール値のリストに変換します

      • 整数のツリーをブールのツリーに変換する

      • 変換NothingNothingJust 7Just 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と呼ばれる論文です。


7
私はMLファンクターとHaskellファンクターの両方を理解していますが、それらを相互に関連付ける洞察力に欠けています。カテゴリー理論的な意味で、これら2つの間の関係は何ですか?
Wei Hu

6
@Wei Hu:カテゴリー理論は私にはまったく意味がありませんでした。私が言える最高のことは、3つの概念すべてがマッピングに関係しているということです。
Norman Ramsey

16
このHaskell wikiによると、en.wikibooks.org / wiki/Haskell /Category_theoryは次のようになります:カテゴリーはオブジェクトと射(関数)のコレクションであり、射はカテゴリー内のオブジェクトからそのカテゴリー内の他のオブジェクトへ。ファンクタは、あるカテゴリのオブジェクトと射を別のカテゴリのオブジェクトと射にマッピングする関数です。少なくともそれが私が理解する方法です。それがプログラミングで正確に何を意味するのか、まだ理解していません。
ポール・

5
@ norman-ramsey、LawvereとSchanuelによるConceptual Mathematicsを見たことがありますか?私はその地域の完全な初心者ですが、この本は非常に読みやすく、あえて言うのは楽しいです。(あなたの説明を愛していました。)
Ram Rajamony 14

2
then you have to be able to take that function and product a related function on collectionsもしかしてproduce代わりにproduct
problemofficer 2018年

64

ここで他の回答は完了ですが、FPによるfunctorの使用について別の説明を試してみましょう。これを類推してください:

ファンクタは、型の容器であるAからマッピング関数を施し、こと→ B、タイプの容器が得られるBを

C ++での抽象化された関数ポインターの使用とは異なり、ここではファンクターは関数ではありません。むしろ、これは、関数さらされたときに一貫して動作するものです。


3
タイプbのコンテナーは、「入力コンテナーと同じ種類のコンテナーですが、現在はbで満たされています」という意味です。したがって、バナナのリストがあり、バナナを取り、フルーツサラダを出力する関数をマップすると、フルーツサラダのリストができます。同様に、バナナの木があり、同じ関数をマップすると、リンゴのができます。など、ツリーリストはここ2つのファンクタです。
Qqwy 2016年

3
「ファンクターは関数の対象となるタイプaのコンテナーです」-実際にはその逆です-関数(射)はファンクターの影響を受け、別の射にマップされます
Dmitri Zaitsev

38

3つの異なる意味があり、あまり関連していません。

  • 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) -> (f a -> f b)ます。これはカテゴリー理論に起源があります。あなたがリンクしたウィキペディアの記事はこの使用法です。

    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のファンクタとはほとんど関係がありません。

  • 命令型言語では、関数へのポインタです。

このコメントの最後の3行の<q> map </ q>は確かに<q> fmap </ q>であるべきではありませんか?
imz-Ivan Zakharyaschev 2010

1
私はファンクターがコンテナーであることをいつも読んでいましたが、これは単純化が不十分なだけです。あなたの答えは最終的にミッシングリンクを提供しました:ファンクターはパラメーター化された型(型コンストラクター)の型クラス(型制約)です。とても簡単です!

16

OCamlでは、パラメーター化されたモジュールです。

C ++を知っている場合は、OCamlファンクタをテンプレートとして考えてください。C ++にはクラステンプレートしかなく、ファンクタはモジュールスケールで機能します。

ファンクタの例はMap.Makeです。module StringMap = Map.Make (String);;文字列キーマップで動作するマップモジュールをビルドします。

ポリモーフィズムだけではStringMapのようなものを実現することはできません。キーについていくつかの仮定を行う必要があります。Stringモジュールには、完全に順序付けられた文字列型の操作(比較など)が含まれ、ファンクターはStringモジュールに含まれる操作にリンクします。オブジェクト指向プログラミングでも同様のことができますが、メソッドの間接参照のオーバーヘッドが発生します。


私はocamlのウェブサイトからそれを得ました-私はパラメーター化されたモジュールの使用がどうなるか理解していません。
Erik Forbes

4
@Kornelええ、私が説明したのはOCamlのコンセプトです。もう1つの概念は、単に「機能的価値」であり、これはFPに特別なことではありません。@Erik少し拡張しましたが、リファレンスドキュメントの読み込みが遅いです。
東武

13

あなたはかなり多くの良い答えを得ました。売り込みます:

数学的な意味でのファンクタは、代数の特殊な関数です。これは、代数を別の代数にマッピングする最小限の関数です。「ミニマリティ」はファンクタ法則で表現されます。

これを見るには2つの方法があります。たとえば、リストはあるタイプのファンクターです。つまり、タイプ 'a'の代数を指定すると、タイプ 'a'のものが含まれるリストの互換性のある代数を生成できます。(例:要素を含むシングルトンリストへの要素を取るマップ:f(a)= [a])ここでも、互換性の概念はファンクタの法則によって表されます。

一方、ファンクターfがタイプaを「オーバー」している場合(つまり、faはタイプaの代数にファンクターfを適用した結果)、gからの関数である場合、次のように計算できます。 faをf bにマップする新しいファンクタF =(fmap g)。つまり、fmapはFの「ファンクターパーツ」を「ファンクターパーツ」にマッピングする部分であり、gは「代数パーツ」を「代数パーツ」にマッピングする関数の一部です。関数、ファンクターが必要です。完了すると、ファンクターでもあります。

言語によってファンクタの概念が異なっているように見えるかもしれませんが、そうではありません。彼らは単に異なる代数に対してファンクタを使用しているだけです。OCamlsにはモジュールの代数があり、その代数に対するファンクターを使用すると、「互換性のある」方法で新しい宣言をモジュールにアタッチできます。

Haskellファンクタは型クラスではありません。これは、型クラスを満たす自由変数を持つデータ型です。データ型(自由変数なし)の根幹を掘り下げたい場合は、データ型を基礎となる代数の関数として再解釈できます。例えば:

データF = F Int

Intsのクラスに同型です。したがって、Fは値コンストラクタとして、Intを同等の代数であるF Intにマップする関数です。ファンクターです。一方、ここでは無料でfmapを入手できません。それがパターンマッチングの目的です。

ファンクタは、代数的に互換性のある方法で、代数の要素にものを「アタッチ」するのに適しています。


8

その質問に対する最良の答えは、Brent Yorgeyによる「Typeclassopedia」にあります。

モナドリーダーのこの号には、ファンクターとは何かの正確な定義と、他の概念の多くの定義や図が含まれています。(Monoid、Applicative、Monadおよびその他の概念はファンクターに関連して説明され、見られます)。

http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf

Typeclassopedia for Functorからの抜粋:「簡単な直感は、Functorがある種の「コンテナー」を表し、コンテナー内のすべての要素に関数を均一に適用する機能を備えている」

しかし、実際にはtypeclassopedia全体が非常に推奨される読み物であり、驚くほど簡単です。ある方法で、そこに提示されたタイプクラスは、特定の動作または機能の語彙を提供するという意味で、オブジェクトのデザインパターンと並行して表示されます。

乾杯


7

InriaのウェブサイトにあるO'Reilly OCamlの本にはかなり良い例があります(これは執筆時点では残念ながらダウンしています)。この本でcaltechが使用している非常に似た例を見つけました:OCamlの紹介(pdfリンク)。関連するセクションはファンクタに関する章です(本の139ページ、PDFの149ページ)。

この本には、リストで構成されるデータ構造を作成するMakeSetと呼ばれるファンクターがあり、要素を追加したり、要素がリストにあるかどうかを判別したり、要素を見つけたりする機能があります。セットに含まれるかどうかを判断するために使用される比較関数はパラメーター化されています(これにより、MakeSetがモジュールではなくファンクターになります)。

また、大文字と小文字を区別しない文字列比較を行うために、比較関数を実装するモジュールもあります。

ファンクターと比較を実装するモジュールを使用すると、新しいモジュールを1行で作成できます。

module SSet = MakeSet(StringCaseEqual);;

大文字と小文字を区別しない比較を使用するセットデータ構造のモジュールを作成します。大文字と小文字を区別する比較を使用するセットを作成する場合は、新しいデータ構造モジュールではなく、新しい比較モジュールを実装するだけで済みます。

東部はファンクターをC ++のテンプレートと比較しました。


6

他の回答と私がこれから投稿するものを考えると、それはかなり過負荷の言葉だと言いますが、とにかく...

Haskellの「functor」という単語の意味に関するヒントについては、GHCiに尋ねてください。

Prelude> :info Functor
class Functor f where
  fmap :: forall a b. (a -> b) -> f a -> f b
  (GHC.Base.<$) :: forall a b. a -> f b -> f a
        -- Defined in GHC.Base
instance Functor Maybe -- Defined in Data.Maybe
instance Functor [] -- Defined in GHC.Base
instance Functor IO -- Defined in GHC.Base

つまり、基本的に、Haskellのファンクターは、マッピングできるものです。別の言い方をすれば、ファンクタはコンテナと見なすことができるものであり、特定の関数を使用して、その関数に含まれる値を変換するように要求できます。このように、リストの、fmapと一致しているmapため、Maybefmap f (Just x) = Just (f x)fmap f Nothing = Nothingなど

Functor typeclassサブセクションおよびFunctors 、Applicative Functors、Monoids on Learn You of a Goodk for Great Goodのセクションは、この特定の概念が役立つ例をいくつか示します。(要約:たくさんの場所!:-))

どのモナドもファンクタとして扱うことができることに注意してください。実際、クレイグスタンツが指摘するように、最もよく使用されるファンクタはモナドである傾向があります... OTOH、型をFunctorタイプクラスのインスタンスにすると便利なことがありますモナドにする手間をかけずに。(例えば、上記のページのいずれかに記載されているZipListからの場合。)Control.Applicative


5

ここでは、プログラミングPOVのファンクタに関する記事を示します。その後に、プログラミング言語でどのように機能するかを具体的に説明します

ファンクタの実用的な使用法はモナドにあり、それを探すとモナドに関する多くのチュートリアルを見つけることができます。


1
「ファンクタの実用的な使い方はモナドにある」だけではありません。すべてのモナドはファンクタですが、非モナドファンクタには多くの用途があります。
amindfv 2013

1
モナドを研究してファンクタを使用することは、ロールスが食料品を買いに行くのを節約するようなものだと思います。
Marco Faustinelli、2015

5

トップ投票の回答へのコメントで、ユーザーWei Huは次のように質問します。

私はMLファンクターとHaskellファンクターの両方を理解していますが、それらを相互に関連付ける洞察力に欠けています。カテゴリー理論的な意味で、これら2つの間の関係は何ですか?

:私はMLを知りませんので、関連する間違いを許して訂正してください。

最初に、「カテゴリー」と「ファンクター」の定義をすべて知っていると仮定します。

簡潔に言えば、「Haskell-functors」は(endo-)functorでありF : Hask -> Hask、「ML-functors」はfunctor G : ML -> ML'です。

ここで、HaskはHaskellの型とその間の関数によって形成されるカテゴリです。同様にMLML'ML構造によって定義されるカテゴリです。

:カテゴリの作成には技術的な問題がいくつかありHaskますが、回避策はいくつかあります。

カテゴリー理論の観点から、これはHask-functorがFHaskell型のマップであることを意味します:

data F a = ...

fmapHaskell関数のマップとともに:

instance Functor F where
    fmap f = ...

MLはほとんど同じですが、fmap私が認識している標準的な抽象化はありません。それを定義してみましょう。

signature FUNCTOR = sig
  type 'a f
  val fmap: 'a -> 'b -> 'a f -> 'b f
end

つまりfマップML-typesとfmapマップするMLので、-functionsを

functor StructB (StructA : SigA) :> FUNCTOR =
struct
  fmap g = ...
  ...
end

ファンクターF: StructA -> StructBです。


5

「Functorは、オブジェクトの構成と、カテゴリの同一性を保持する射をマッピングします。」

カテゴリとは何ですか?

たくさんのオブジェクトです!

円の中に数個のドット(今のところ2個のドット、1個は「a」、もう1個は「b」)を描き、その円にA(Category)という名前を付けます。

カテゴリは何を保持していますか?

オブジェクト間の構成と、すべてのオブジェクトのIdentity関数。

したがって、Functorを適用した後、オブジェクトをマップし、コンポジションを保持する必要があります。

'A'がオブジェクト['a'、 'b']を持つカテゴリであり、射が存在することを想像してみましょうa-> b

次に、これらのオブジェクトと射を別のカテゴリ「B」にマッピングできるファンクタを定義する必要があります。

ファンクターは「多分」と呼ばれているとしましょう

data Maybe a = Nothing | Just a

したがって、カテゴリ「B」は次のようになります。

別の円を描いてください。ただし、今回は「a」と「b」の代わりに「Maybe a」と「Maybe b」を使用します。

すべてが良さそうで、すべてのオブジェクトがマッピングされています

「a」は「Maybe a」になり、「b」は「Maybe b」になりました。

しかし、問題は、射を 'a'から 'b'にマッピングする必要があることです。

つまり、「A」の射a-> bは射「多分a」->「多分b」にマッピングする必要があります。

aからの射-> bはfと呼ばれ、次に '多分a'からの射-> '多分b'は 'fmap f'と呼ばれます

次に、「A」で「f」が行っていた機能を確認し、「B」でそれを複製できるかどうかを確認します。

「A」における「f」の関数定義:

f :: a -> b

fはaを取り、bを返す

「B」内の「f」の関数定義:

f :: Maybe a -> Maybe b

fは多分aを取り、多分bを返す

fmapを使用して関数 'f'を 'A'から 'B'の関数 'fmap f'にマッピングする方法を見てみましょう

fmapの定義

fmap :: (a -> b) -> (Maybe a -> Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = Just(f x)

では、ここで何をしているのですか?

タイプ 'a'の 'x'に関数 'f'を適用しています。「なし」の特別なパターンマッチングは、Functor Maybeます。

したがって、オブジェクト[a、b]と射[f]をカテゴリ 'A'からカテゴリ 'B'にマッピングしました。

そのファンクター!

ここに画像の説明を入力してください


興味深い答え。私はそれをモナドで補完したいのですが、ブリトー抽象化、直感、および「モナドチュートリアルの誤解」への面白い答え)と彼の文「ファンクターFは各タイプTを受け取り、それを新しいタイプFTにマップします」、つまりタイプコンストラクター。関数型プログラミングとカテゴリー理論-カテゴリーとファンクターも役に立ちました。
Ludovic Kuty

3

大まかな概要

関数型プログラミングでは、ファンクタは基本的に、通常の単項関数(つまり、1つの引数を持つ関数)を新しい型の変数間の関数に持ち上げる構造です。プレーンなオブジェクト間で単純な関数を記述および維持し、ファンクタを使用してそれらを持ち上げてから、複雑なコンテナオブジェクト間で関数を手動で記述する方がはるかに簡単です。さらなる利点は、単純な関数を1回だけ記述し、その後、異なるファンクタを介してそれらを再利用することです。

ファンクタの例には、配列、「たぶん」と「どちらか」のファンクタ、フューチャー(例:https : //github.com/Avaq/Flutureを参照)、およびその他多数が含まれます。

姓名から完全な人物の名前を構成する関数を考えてみます。fullName(firstName, lastName)2つの引数の関数のように定義することもできますが、1つの引数の関数のみを扱うファンクタには適していません。修正するには、すべての引数を単一のオブジェクトnameに収集します。これは、関数の単一の引数になります。

// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName

配列に多数の人がいる場合はどうでしょうか?手動でリストを調べる代わりに、短いコード1行の配列用に提供されfullNameているmapメソッドを介して関数を再利用できます。

fullNameList = nameList => nameList.map(fullName)

そしてそれを

nameList = [
    {firstName: 'Steve', lastName: 'Jobs'},
    {firstName: 'Bill', lastName: 'Gates'}
]

fullNames = fullNameList(nameList) 
// => ['Steve Jobs', 'Bill Gates']

これは、私たちのすべてのエントリがとプロパティのnameList両方firstNameを提供するオブジェクトである場合はいつでも機能しlastNameます。しかし、いくつかのオブジェクトがそうでない場合(またはまったくオブジェクトでない場合)はどうなりますか?エラーを回避してコードをより安全にするために、オブジェクトをMaybeタイプにラップすることができます(例:https : //sanctuary.js.org/#maybe-type):

// function to test name for validity
isValidName = name => 
    (typeof name === 'object') 
    && (typeof name.firstName === 'string')
    && (typeof name.lastName === 'string')

// wrap into the Maybe type
maybeName = name => 
    isValidName(name) ? Just(name) : Nothing()

ここで、Just(name)は有効な名前のみを保持するコンテナであり、Nothing()他のすべてに使用される特別な値です。ここで、引数の有効性を確認するために中断(または忘れる)する代わりfullNameに、mapメソッドに基づいて、元の関数を別の1行のコードで再利用(リフト)できます。今回はMaybe型に提供されます。

// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)

そしてそれを

justSteve = maybeName(
    {firstName: 'Steve', lastName: 'Jobs'}
) // => Just({firstName: 'Steve', lastName: 'Jobs'})

notSteve = maybeName(
    {lastName: 'SomeJobs'}
) // => Nothing()

steveFN = maybeFullName(justSteve)
// => Just('Steve Jobs')

notSteveFN = maybeFullName(notSteve)
// => Nothing()

カテゴリー理論

ファンクタ圏論は、その射の構図を尊重二つのカテゴリー間のマップです。コンピュータ言語、関心のメインカテゴリは、その一つであるオブジェクトであるタイプ(値の特定のセット)、及びそのな機能f:a->b一つのタイプからa別のタイプへb

たとえばaString型、b数値型であるとします。これfは、文字列をその長さにマッピングする関数です。

// f :: String -> Number
f = str => str.length

ここでa = Stringは、すべての文字列b = Numberのセットとすべての数値のセットを表します。その意味で、abは両方ともセットカテゴリのオブジェクトを表します(タイプのカテゴリに密接に関連しており、違いはここでは重要ではありません)。集合カテゴリでは、2つの集合間のは、最初の集合から2番目の集合までのすべての関数です。したがって、fここでの長さ関数は、文字列のセットから数値のセットへの射です。

セットカテゴリのみを考慮するため、関連するファンクタはそれ自体から、オブジェクトにオブジェクトを送信し、射を射に変換するマップであり、特定の代数法則を満たします。

例: Array

Array多くのことを意味する可能性がありますが、Functorは1つだけです。つまり、型構成、a[a]のすべての配列の型への型のマッピングaです。たとえば、Arrayファンクタは型String を型[String](任意の長さの文字列のすべての配列のセット)にマップし、型をNumber対応する型[Number](数値のすべての配列のセット)に設定します。

Functorマップを混同しないようにすることが重要です

Array :: a => [a]

射付きでa -> [a]。ファンクタは、単純に型を型に型aにマップ(関連付け)し[a]ます。各タイプが実際には要素のセットであることは、ここでは関係ありません。対照的に、射はそれらのセット間の実際の関数です。たとえば、自然な射(関数)があります。

pure :: a -> [a]
pure = x => [x]

これは、値を1つのエントリとして1要素の配列に送信します。その関数はFunctorの一部ではありませんArray!このファンクターの観点から見るとpure、他の関数と同じように機能するだけであり、特別なものはありません。

一方、ArrayFunctorには2番目の部分、つまり射の部分があります。これは、射f :: a -> bを射に写像し[f] :: [a] -> [b]ます:

// a -> [a]
Array.map(f) = arr => arr.map(f)

ここでarr型の値を有する任意の長さの任意の配列でありa、そしてarr.map(f)型の値と同じ長さの配列であるbエントリ適用した結果である、fのエントリにarr。ファンクタにするには、アイデンティティをアイデンティティに、組成を組成にマッピングする数学的法則が成り立つ必要があります。これは、このArray例では簡単に確認できます。


2

以前の理論的または数学的な答えと矛盾しないが、ファンクターは1つのメソッドのみを持ち、関数として効果的に使用されるオブジェクトでもあります(オブジェクト指向プログラミング言語の場合)。

例としては、JavaのRunnableインターフェースがあり、runメソッドしかありません。

ファーストクラスの機能を持つJavascriptの最初の例を考えてみましょう。

[1, 2, 5, 10].map(function(x) { return x*x; });

出力:[1、4、25、100]

mapメソッドは関数を取り、各要素が元の配列の同じ位置にある値にその関数を適用した結果である新しい配列を返します。

同じことをJavaで行うには、Functorを使用して、最初にインターフェースを定義する必要があります。

public interface IntMapFunction {
  public int f(int x);
}

次に、マップ関数を持つコレクションクラスを追加すると、次のようになります。

myCollection.map(new IntMapFunction() { public int f(int x) { return x * x; } });

これは、IntMapFunctionのインラインサブクラスを使用してFunctorを作成します。これは、以前のJavaScriptの例の関数に相当するOOです。

Functorを使用すると、OO言語で関数型テクニックを適用できます。もちろん、一部のオブジェクト指向言語は関数も直接サポートしているため、これは必須ではありません。

リファレンス:http : //en.wikipedia.org/wiki/Function_object


実際、「関数オブジェクト」はファンクターの正しい説明ではありません。たとえばArray、ファンクタですが、Array(value)1要素の配列しか提供しません。
Dmitri Zaitsev

0

キッス:ファンクタは、マップメソッドを持つオブジェクトです。

JavaScriptの配列はマップを実装するため、ファンクターです。プロミス、ストリーム、ツリーは、関数型言語でマップを実装することが多く、実装するとファンクターと見なされます。ファンクターのmapメソッドは、独自のコンテンツを取得し、mapに渡された変換コールバックを使用してそれぞれを変換し、最初のファンクターとしての構造を含むが、変換された値を持つ新しいファンクターを返します。

src:https : //www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76


1
「オブジェクト」は非常に広く取られるべきであり、単に「何か」を意味することに注意してください。たとえば、OOP言語の場合は、クラスの代わりにオブジェクトを使用します。「ファンクターはファンクターインターフェースを実装するクラスである」と言うことができます(もちろん、このインターフェースは物理的には存在しないかもしれませんが、「マップ」ロジックをそのインターフェースに持ち上げて、マッピング可能なすべてのクラスで共有することができます-タイプシステムがこの一般的なタイプ入力を許可する限り、つまり)。
Qqwy 2016年

1
クラスは正直に言うと非常に混乱しますが、一方では具体的な何かの青写真にすぎませんが、メソッド(静的なもの)があり、オブジェクトのように動作する場合もあります。クラスは、インターフェースまたはそれが作成するインスタンスを実装していますか?
soundyogi

1
はい、混乱する可能性があります。ただし、クラスインターフェースを実装します(インターフェースメソッドで指定された空白を「埋めます」。言い換えると、インターフェースの抽象的なガイドラインを、即座に(しゃれを許して)インスタンス化できる具体的なガイドラインに変えます)。「クラスはオブジェクトのように動作する」について:Rubyのような真のOOP言語では、クラスは「クラス」クラスのインスタンスです。カメはずっと下にいます。
Qqwy 2016年

Array型構成は単一のファンクタを定義します。そのインスタンスは「配列」とも呼ばれますが、ファンクタではありません。ここでの説明は、より正確にする必要があります。
Dmitri Zaitsev

@DmitriZaitsev詳しく説明していただけますか?つまり、インスタンスはファンクタではないということです。1つをマッピングして新しいファンクターを取得するので、その意味はわかりません。
サウンドヨギ

-4

実際には、ファンクターはC ++で呼び出し演算子を実装するオブジェクトを意味します。ocamlでは、ファンクターはモジュールを入力として受け取り、別のモジュールを出力するものを指すと思います。


-6

簡単に言えば、ファンクタ、つまり関数オブジェクトは、関数のように呼び出すことができるクラスオブジェクトです。

C ++の場合:

これはあなたが関数を書く方法です

void foo()
{
    cout << "Hello, world! I'm a function!";
}

これがファンクタを書く方法です

class FunctorClass
{
    public:
    void operator ()
    {
        cout << "Hello, world! I'm a functor!";
    }
};

これでこれを行うことができます:

foo(); //result: Hello, World! I'm a function!

FunctorClass bar;
bar(); //result: Hello, World! I'm a functor!

これらの機能が非常に優れているのは、クラスの状態を維持できることです。関数が呼び出された回数を関数に尋ねたい場合を想像してみてください。きちんとカプセル化された方法でこれを行う方法はありません。関数オブジェクトを使用すると、他のクラスと同じように、インクリメントするインスタンス変数とoperator ()その変数を検査するメソッドがあり、すべてがきちんと整っています。


12
いいえ、これらのファンクターは、FP言語で使用されている型理論の概念ではありません。
東武

1
FunctorClass最初のファンクター法を満たしていることをどのように証明できるかが少しわかりますが、第2法の証明をスケッチできますか?よくわかりません。
イェルクWミッターク

3
ああ、君たちは正しい。私は「ウェブが非常に技術的な説明を提供した」ことを突き刺し、「MLファミリーの言語では、ファンクターは1つ以上の他のモジュールをパラメーターとしてとるモジュールです」と避けようと試みました。しかし、この答えは良くありません。過度に単純化され、十分に指定されていない。私はそれを激怒削除したくなりますが、次の世代に首を振るように残しておきます:)
Matt

問題を解決するのに役立つので、回答とコメントを残していただき、ありがとうございます。ありがとうございました!私は答えのほとんどがHaskellまたはOCamlの観点から書かれていること、そして私にとってワニをワニの観点から説明することに少し似ていることに問題があります。
Rob

-10

Functorは特に関数型プログラミングとは関係ありません。これは、関数またはある種のオブジェクトへの単なる「ポインタ」であり、関数のように呼び出すことができます。


8
(カテゴリー理論からの)ファンクターの特定のFPの概念がありますが、同じ単語が非FP言語の他のものにも使用されているのは正しいです。
Craig Stuntz、2010年

関数ポインタがファンクタであることを確信していますか?関数ポインターが2つのファンクターの法則、特に第2ファンクターの法則(射の構成の保持)をどのように満たすかはわかりません。その証拠はありますか?(大まかなスケッチです。)
JörgW Mittag 2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.