Fonksiyonel Programlamada fonksiyoner nedir?


224

Fonksiyonel programlama hakkındaki çeşitli makaleleri okurken birkaç kez 'Functor' terimiyle karşılaştım, ancak yazarlar genellikle okuyucunun terimi zaten anladığını varsayarlar. Web'e bakmak, aşırı teknik açıklamalar ( Wikipedia makalesine bakın ) veya inanılmaz derecede belirsiz açıklamalar (bu okaml-tutorial web sitesinde Functors bölümüne bakın ) sağladı .

Birisi terimi nazikçe tanımlayabilir, kullanımını açıklayabilir ve belki de Functor'ların nasıl yaratıldığı ve kullanıldığına dair bir örnek verebilir mi?

Edit : Terimin arkasındaki teori ile ilgilenirken, ben teorinin uygulanması ve pratik kullanımı daha teori ile daha az ilgileniyorum.

Edit 2 : Bazı çapraz terminoliji var gibi görünüyor: Özellikle C ++ fonksiyon nesneleri değil, fonksiyonel programlama Functors atıfta bulunuyorum.


4
Ayrıca bakınız: adit.io/posts/…
Impala Vlad

Oldukça iyi bir cevap da: stackoverflow.com/a/45149475/1498178
toraritte

Konseptin arkasındaki stratosferik terminoloji ve teoriye göre pratik uygulama ve kullanımla daha fazla ilgileniyorsanız, sadece bir astara ihtiyacınız vardır: bir işlev bir "harita" işlevi ortaya koyar.
Richard Gomes

@RichardGomes IMHO Bence bir functor rolü değil basit bir Java benzeri arayüze azaltır. Bir functor şeyleri dönüştürür, mevcut olanlardan (Haskell'de) yeni türler oluşturur, bu da türlerin eşlendiği anlamına gelir. fmapfonksiyonları eşler. İki tür eşleme söz konusudur. Bu tür şeyleri görmenin yolu, kategori teorisini anlamaya yardımcı olacaktır (ki bu daha geneldir). Yani Haskell'deki tüm kategori teorisi maddelerinde (functor, monads, ...) bize yardımcı olacak temel kategori teorisini anlamak ilginç.
Ludovic Kuty

@VladtheImpala Blog yazısı harika ama çok yardımcı olsa bile, bir functor'ın başka bir tür oluşturduğunu (eşlediğini) unutmayın. Özellikle, bir Functor F her T tipini alır ve yeni bir FT tipiyle eşleştirir " cümlesini severim . IMHO, bu gibi şeyleri görmek pratik olsa bile, bir değer etrafında bir bağlam (bir kutu) değildir (Haskell PoV vs kategori teorisi PoV?)
Ludovic Kuty

Yanıtlar:


273

"Functor" kelimesi, matematiğin çok genel, çok soyut bir dalı olan kategori teorisinden gelir. İşlevsel dil tasarımcıları tarafından en az iki farklı şekilde ödünç alınmıştır.

  • ML dil ailesinde, functor, bir veya daha fazla modülü parametre olarak alan bir modüldür. Gelişmiş bir özellik olarak kabul edilir ve çoğu yeni programcı onunla zorluk çeker.

    Uygulama ve pratik kullanım örneği olarak, dengeli ikili arama ağacının en sevdiğiniz formunu bir kez ve herkes için bir işlev olarak tanımlayabilirsiniz ve bir parametre olarak aşağıdakileri sağlayan bir modül alır:

    • İkili ağaçta kullanılacak anahtar türü

    • Tuşlarda toplam sipariş fonksiyonu

    Bunu yaptıktan sonra, aynı dengeli ikili ağaç uygulamasını sonsuza kadar kullanabilirsiniz. (Ağaçta saklanan değer türü genellikle polimorfiktir - ağacın onları kopyalamaktan başka değerlere bakması gerekmez, oysa ağacın anahtarları kesinlikle karşılaştırabilmesi gerekir ve karşılaştırma işlevini alır. functor parametresi.)

    ML işlevlerinin başka bir uygulaması da katmanlı ağ protokolleridir . Bağlantı CMU Fox grubu tarafından gerçekten müthiş bir makaledir; daha basit katmanlar (IP gibi, hatta doğrudan Ethernet üzerinden) üzerinde daha karmaşık protokol katmanları (TCP gibi) oluşturmak için işlevlerin nasıl kullanılacağını gösterir. Her katman, altındaki katmanı parametre olarak alan bir işlev olarak uygulanır. Yazılımın yapısı, aslında sadece programcının zihninde mevcut olan katmanların aksine, insanların problem hakkında düşünme şekillerini yansıtır. 1994 yılında bu çalışma yayınlandığında büyük bir mesele oldu.

    ML functorlarının vahşi bir örneği için , iş yerinde functorların yayınlanabilir (yani korkutucu) bir örneğini içeren ML Module Mania kağıdını görebilirsiniz . ML modülleri sisteminin parlak, net, pellucid açıklaması (diğer modüllerle karşılaştırmalı olarak) için Xavier Leroy'un parlak 1994 POPL kağıdı Manifest Türleri, Modülleri ve Ayrı Derleme'nin ilk birkaç sayfasını okuyun .

  • Haskell'de ve ilgili bazı saf işlevsel dilde Functorbir tür sınıftır . Bir tür, belirli işlemleri beklenen belirli davranışlarla sağladığında, bir tür sınıfına (veya daha teknik olarak "tür", "tür sınıfının bir örneğidir) aittir. Bir koleksiyon, koleksiyon gibi belirli bir davranışı varsa Tsınıfa ait olabilir Functor:

    • Tip Tkoleksiyonun eleman türü olarak düşünmek gerekir başka bir tür üzerinde parametreli. Tam koleksiyonun tipi sonra gibi bir şey T Int, T String, T Bool, sırasıyla tamsayılar, dizeleri veya gibi Boolean içeren eğer. Eleman tipi bilinmiyorsa, bir olarak yazılır tipi parametresi a olduğu gibi T a.

      Örnekler arasında listeler (sıfır veya daha fazla tür öğesi a), Maybetür (sıfır veya bir tür öğesi a), tür öğelerinin grupları, tür öğelerinin adizileri, atür değerleri içeren her türlü arama ağacı ave diğer birçok düşünebilir.

    • Karşılanması gereken diğer özellik, Tbir tür işleve a -> b(elemanlar üzerinde bir işlev) sahipseniz, o işlevi alıp koleksiyonlarda ilgili bir işlevi üretebilmenizdir. Bunu fmap, Functortype sınıfındaki her tür tarafından paylaşılan işleç ile yaparsınız . Operatör aslında aşırı yüklenmiş, bu nedenle eventipte bir fonksiyonunuz varsa Int -> Bool,

      fmap even

      birçok harika şey yapabilen aşırı yüklenmiş bir işlevdir:

      • Tamsayıların listesini Booleans listesine dönüştürme

      • Bir tamsayı ağacını Booleans ağacına dönüştürme

      • Dönüştürme Nothingiçin Nothingve Just 7hiçJust False

      Haskell'de, bu özellik aşağıdakilerin türünü vererek ifade edilir fmap:

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

      şimdi küçük bir sınıfımız var t, yani " Functorsınıftaki herhangi bir tür ".

    Uzun bir öyküyü kısaltmak gerekirse, Haskell'de bir işlev, öğeler üzerinde bir işlev verildiğinde fmap, koleksiyonlar üzerinde size bir işlev verecek olan bir tür koleksiyon . Tahmin edebileceğiniz gibi, bu yaygın olarak yeniden kullanılabilecek bir fikir, bu yüzden Haskell'in standart kütüphanesinin bir parçası olarak kutsanmıştır.

Her zamanki gibi, insanlar yeni, faydalı soyutlamalar icat etmeye devam ve içine bakmak isteyebilirsiniz uygulamalı iyi referans olarak adlandırılan bir kağıt olabilir hangi funktorlar, Effects ile Uygulamalı Programlama Conor McBride ve Ross Paterson tarafından.


7
Hem ML-functor'ları hem de Haskell-functorları anlıyorum, ancak bunları bir araya getirme anlayışından yoksundur. Kategori-teorik anlamda bu ikisi arasındaki ilişki nedir?
Wei Hu

6
@Wei Hu: Kategori teorisi bana hiç mantıklı gelmedi. Söyleyebileceğim en iyi şey, her üç kavramın da haritalamayı içermesidir.
Norman Ramsey

16
Bu Haskell wiki'sine göre: en.wikibooks.org/wiki/Haskell/Category_theory , şöyle: Bir kategori, morfizmlerin bir kategorideki nesnelerden o kategorideki diğer nesnelere ait olduğu bir nesne ve morfizm (fonksiyon) koleksiyonudur. . Functor, bir kategorideki nesneleri ve morfizmi başka kategorideki nesnelere ve morfizmlere eşleyen bir fonksiyondur. En azından ben böyle anlıyorum. Bunun tam olarak programlama için ne anlama geldiğini anlamadım.
paul

5
@ norman-ramsey, Lawvere ve Schanuel'in Kavramsal Matematiğine baktınız mı? Ben bölgeye toplam acemi ama kitap son derece okunabilir ve - cesaret-ı-demek - keyifli. (Açıklamanızı sevdim.)
Ram Rajamony

2
then you have to be able to take that function and product a related function on collectionsŞunu mu demek istediniz produceyerine product?
sorunlu memur

64

Buradaki diğer cevaplar tamam, ancak FP'nin functor kullanımıyla ilgili başka bir açıklama deneyeceğim . Bunu analoji olarak kabul et:

Bir funktoru tipi bir kaptır a gelen haritalar bir fonksiyonuna tabi ki , birb , tipi bir kap elde edilir , b .

C ++ 'da soyutlanmış işlev işaretçisi kullanımından farklı olarak, burada işlev işlev değildir ; daha ziyade, bir işleve tabi tutulduğunda tutarlı davranan bir şeydir .


3
B tipi bir kap, "giriş kabı ile aynı tür kap anlamına gelir, fakat şimdi b'lerle doldurulur" anlamına gelir. Bu yüzden bir muz listemiz varsa ve bir muz alan ve bir meyve salatası veren bir işlevi haritalıyorsak , şimdi meyve salatalarının bir listesine sahibiz. Aynı şekilde, bir muz ağacımız olsaydı ve aynı işlevi haritalasaydık , şimdi bir elma ağacımız olurdu . Vb ağaç ve liste burada iki Functor vardır.
Qqwy

3
"Functor, bir işleve tabi tutulduğunda a tipi bir kaptır" - aslında bunun tersi de geçerlidir - işlev (morfizm) başka bir morfizmle eşlenecek bir functor'a tabidir
Dmitri Zaitsev

38

Çok farklı olmayan üç farklı anlam var!

  • Ocaml'de parametreli bir modüldür. El kitabına bakın . Ben onları grok için en iyi yolu örnek olduğunu düşünüyorum: (hızlı bir şekilde yazılmış, buggy olabilir)

    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);;

Artık hızlı bir şekilde olası birçok sipariş ekleyebilir, yeni siparişler oluşturmanın yollarını, bunların üzerine kolayca ikili veya doğrusal arama yapabilirsiniz. Genel programlama FTW.

  • Haskell gibi fonksiyonel programlama dillerinde, "eşlenebilen" bazı tip yapıcıları (listeler, kümeler gibi parametreli tipler) anlamına gelir. Kesin olmak gerekirse, bir functor file donatılmıştır (a -> b) -> (f a -> f b). Bunun kategori teorisinin kökenleri vardır. Bağlantı verdiğiniz Wikipedia makalesi bu kullanımdır.

    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

Yani, bu özel bir tür inşaatçılar ve Ocaml'deki fonksiyonlarla çok az ilgisi var!

  • Zorunlu dillerde, işlev gösterecek bir göstericidir.

Bu yorumun son 3 satırında <q> harita </q> gerçekten <q> fmap </q> olmamalı mı?
imz - Ivan Zakharyaschev

1
Her zaman functorların kaplar olduğunu okudum - ama bu sadece zayıf bir basitleştirme. Yanıtınız sonunda eksik bağlantıyı sağladı: Functors, parametreleştirilmiş türler (tür yapıcılar) için bir tür sınıfıdır (tür kısıtlaması). Bu kadar basit!

16

OCaml'de, parametrelendirilmiş bir modüldür.

C ++ biliyorsanız, bir OCaml işlevini şablon olarak düşünün. C ++ sadece sınıf şablonlarına sahiptir ve functors modül ölçeğinde çalışır.

Functor örneği Map.Make; module StringMap = Map.Make (String);;String anahtarlı haritalarla çalışan bir harita modülü oluşturur.

Sadece polimorfizmle StringMap gibi bir şey başaramadınız; tuşlar üzerinde bazı varsayımlar yapmanız gerekir. Dize modülü, tamamen sıralı bir dize türündeki işlemleri (karşılaştırma vb.) İçerir ve işlev Dize modülünün içerdiği işlemlere bağlanır. Nesne yönelimli programlama ile benzer bir şey yapabilirsiniz, ancak yöntem dolaylı yükü olurdu.


Bunu ocaml web sitesinden aldım - ama parametreli bir modülün ne olacağını anlamıyorum.
Erik Forbes

4
@Kornel Evet, tarif ettiğim bir OCaml konsepti. Diğer kavram sadece “işlevsel değer” dir, bu FP'de özel bir şey değildir. @Erik Hafifçe genişledim, ancak referans belgelerinin yüklenmesi yavaş.
Tobu

13

Birkaç iyi cevabınız var. Sahaya gireceğim:

Matematiksel anlamda bir functor, bir cebir üzerinde özel bir işlev türüdür. Bir cebiri başka bir cebire eşleyen minimal bir fonksiyondur. "Minimalite" işlev yasaları ile ifade edilir.

Buna bakmanın iki yolu var. Örneğin, listeler bazı türler üzerindeki işlevlerdir. Yani, 'a' tipi üzerinde bir cebir verildiğinde, 'a' tipi şeyler içeren uyumlu bir liste cebiri oluşturabilirsiniz. (Örneğin: bir öğeyi içeren tek bir listeye götüren harita: f (a) = [a]) Yine, uyumluluk kavramı işlev yasaları tarafından ifade edilir.

Öte yandan, a tipi a "üzerinden" bir functor verildiğinde (yani fa, f ftortorunu a tipi cebire uygulamanın sonucudur) ve g: a -> b fonksiyonundan hesaplayabiliriz. fa'yi f ile eşleyen yeni bir F = (fmap g) işlevi b. Kısacası, fmap, F'nin "functor parçalarını" "functor parçalarını" eşleyen kısmıdır ve g, "cebir parçalarını" "cebir parçalarını" eşleyen fonksiyonun bir parçasıdır. Bir işlev, bir işlev alır ve tamamlandığında, aynı zamanda bir işlevdir.

Farklı diller farklı işlevler kullanmaktadır gibi görünebilir, ancak değildir. Sadece farklı cebirlerde functor kullanıyorlar. OCamls modüllerin bir cebirine sahiptir ve bu cebirin üzerindeki işlevler, bir modüle "uyumlu" bir şekilde yeni bildirimler eklemenizi sağlar.

Haskell işlevi bir tür sınıfı DEĞİLDİR. Type sınıfını karşılayan serbest değişkenli bir veri türüdür. Bir veri tipinin bağırsaklarını (serbest değişkenler olmadan) kazmak istiyorsanız, bir veri türünü temeldeki bir cebirin üzerinde bir fonktor olarak yeniden yorumlayabilirsiniz. Örneğin:

veri F = F Int

Ints sınıfına göre izomorfiktir. Yani, bir değer yapıcı olarak F, eşdeğer bir cebir olan Int ile F Int'yi eşleyen bir işlevdir. Bu bir işlevcidir. Öte yandan, burada ücretsiz fmap alamıyorsunuz. Örüntü eşleştirme bunun içindir.

Functors, cebirsel olarak uyumlu bir şekilde şeyleri cebirsel öğelere "bağlamak" için iyidir.


8

Bu sorunun en iyi cevabı Brent Yorgey tarafından "Typeclassopedia" da bulunmaktadır.

Monad Reader'ın bu sayısı, bir fonksiyonun ne olduğunun kesin bir tanımını ve diğer kavramların bir tanımını ve bir diyagramı içerir. (Monoid, Uygulamalı, Monad ve diğer kavramlar bir fonksiyonla ilişkili olarak açıklanır ve görülür).

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

Functor için Typeclassopedia'dan alıntı: "Basit bir sezgi, bir Functor'ın bir tür" kap "ı temsil etmesinin yanı sıra, kaptaki her öğeye eşit olarak bir işlev uygulama yeteneğidir"

Ama gerçekten tüm daktilo sınıfı medya, şaşırtıcı derecede kolay olan şiddetle tavsiye edilen bir okuma. Bir şekilde, burada sunulan tip sınıfını, size verilen davranış veya yetenek için bir kelime dağarcığı sağladıkları için nesnede tasarım desenine paralel olarak görebilirsiniz.

Şerefe


7

Inria'nın web sitesinde bulunan O'Reilly OCaml kitabında oldukça iyi bir örnek var (yazarken maalesef aşağıda). Caltech tarafından kullanılan bu kitapta çok benzer bir örnek buldum: OCaml'a giriş (pdf bağlantısı) . İlgili bölüm, işleçler hakkındaki bölümdür (kitapta Sayfa 139, PDF'de sayfa 149).

Kitapta, bir liste içeren bir veri yapısı oluşturan ve öğe ekleme, öğenin listede olup olmadığını belirleme ve öğeyi bulma işlevleri yapan MakeSet adlı bir işlevi vardır. Kümede olup olmadığını belirlemek için kullanılan karşılaştırma işlevi parametrelerle ifade edilmiştir (bu, MakeSet'i bir modül yerine bir functor yapar).

Ayrıca, büyük / küçük harf duyarsız bir dize karşılaştırması yapmak için karşılaştırma işlevini uygulayan bir modül vardır.

Functor ve karşılaştırmayı uygulayan modülü kullanarak bir satırda yeni bir modül oluşturabilirler:

module SSet = MakeSet(StringCaseEqual);;

büyük / küçük harfe duyarlı olmayan karşılaştırmalar kullanan bir küme veri yapısı için bir modül oluşturur. Büyük / küçük harfe duyarlı karşılaştırmalar kullanan bir küme oluşturmak istiyorsanız, yeni bir veri yapısı modülü yerine yeni bir karşılaştırma modülü uygulamanız gerekir.

Tobu, oldukça uygun olduğunu düşündüğüm C ++ şablonlarıyla functors karşılaştırdı.


6

Diğer cevaplar ve şimdi yazacağım şey göz önüne alındığında, oldukça aşırı yüklenmiş bir kelime olduğunu söyleyebilirim, ama yine de ...

Haskell'deki 'functor' kelimesinin anlamı hakkında bir ipucu için GHCi'ye sorun:

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

Yani, temel olarak, Haskell'deki bir işlev haritalanabilecek bir şeydir. Bunu söylemenin başka bir yolu, bir işleç, içerdiği değeri dönüştürmek için belirli bir işlevi kullanması istenebilen bir kap olarak kabul edilebilecek bir şeydir; Böylece, listelerin, fmapdenk olan map, için Maybe, fmap f (Just x) = Just (f x), fmap f Nothing = Nothingvb

Functor typeclass alt bölümü ve Functor'lar, Uygulamalı Functors ve Monoidler bölümü ait Büyük İyi için You bir Haskell bilgi bu özel konsept kullanışlı olduğu bazı örnekler vermek. (Özet: birçok yer! :-))

Herhangi bir monadın bir functor olarak ele alınabileceğini ve aslında Craig Stuntz'un işaret ettiği gibi, en sık kullanılan functorların monad olma eğilimindedir ... OTOH, bir tipin Functor tip sınıfının bir örneğini yapmak uygundur. Monad yapma zahmetine girmeden. (Halinde Örnek ZipListgelen Control.Applicativebahsedilen, yukarıda belirtilen sayfalara biri ).


5

İşte bir programlama POV'sinden gelen functors hakkında bir makale , ardından daha spesifik olarak programlama dillerinde nasıl yüzeyleştikleri .

Bir functor'un pratik kullanımı bir monaddadır ve eğer bakarsanız monadlar üzerinde birçok öğretici bulabilirsiniz.


1
"Bir işlevin pratik kullanımı bir monaddadır" - sadece değil. Tüm monadlar functor'dur, ancak monad olmayan functorların birçok kullanımı vardır.
amindfv

1
Functor kullanmak için monads incelemek bakkal satın almak için bir Rolls tasarruf gibi olduğunu söyleyebilirim.
Marco Faustinelli

5

En çok oy alan cevabın yorumunda , kullanıcı Wei Hu şöyle soruyor:

Hem ML-functor'ları hem de Haskell-functorları anlıyorum, ancak bunları bir araya getirme anlayışından yoksundur. Kategori-teorik anlamda bu ikisi arasındaki ilişki nedir?

Not : ML'yi bilmiyorum, bu yüzden lütfen ilgili hataları affedin ve düzeltin.

Başlangıçta hepimizin 'kategori' ve 'işlev' tanımlarına aşina olduğumuzu varsayalım.

Kompakt bir cevap "Haskell-functors" (endo-) functor F : Hask -> Hask, "ML-functors" functorG : ML -> ML' .

Burada, Haskbenzer Haskell türleri ve aralarındaki fonksiyonları ve oluşturduğu kategoridir MLve ML'ML yapılar tarafından tanımlanmış kategoriler bulunmaktadır.

Not : Bir kategori oluşturmayla ilgili bazı teknik sorunlarHask vardır, ancak bunların çevresinde yollar vardır.

Kategori teorik bakış açısından, bu bir Hask-functor'ın FHaskell tiplerinin bir haritası olduğu anlamına gelir :

data F a = ...

fmapHaskell fonksiyonlarının bir haritası ile birlikte :

instance Functor F where
    fmap f = ...

ML, hemen hemen aynıdır, ancak fmapfarkında olduğum kanonik soyutlama olmamasına rağmen , bir tanesini tanımlayalım:

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

Bu fharitalar ML-tipler vefmap haritalar MLyüzden, -functions

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

bir işleçtir F: StructA -> StructB.


5

"Functor, bir kategorinin kompozisyonunu ve kimliğini koruyan nesnelerin ve morfizmlerin haritalanmasıdır."

Bir kategori nedir tanımlayalım?

Bu bir sürü nesne!

Bir daireye birkaç nokta çizin (şimdilik 2 nokta, biri 'a' diğeri 'b') ve o daireye A (Kategori) adını verin.

Kategori ne tutar?

Nesneler arasındaki kompozisyon ve her nesne için Kimlik fonksiyonu.

Bu nedenle, Functor'umuzu uyguladıktan sonra nesneleri eşleştirmeli ve kompozisyonu korumalıyız.

Hayal edelim ki 'A', ['a', 'b'] nesneleri olan bir kategorimizdir ve a -> b biçiminde bir morfizm vardır.

Şimdi, bu nesneleri ve morfizmaları başka bir 'B' kategorisine ayırabilecek bir işlev tanımlamalıyız.

Diyelim ki functor 'Belki'

data Maybe a = Nothing | Just a

Yani, 'B' kategorisi buna benziyor.

Lütfen bu sefer 'a' ve 'b' yerine 'Belki' ve 'Belki' ile başka bir daire çizin.

Her şey iyi görünüyor ve tüm nesneler haritalanıyor

'a' belki 'a' ve 'b' 'Belki b' oldu.

Ama sorun şu ki, morfizmi 'a' dan 'b' ye de eşlemeliyiz.

Bu, "A" daki a -> b biçiminin, "Belki" -> "Belki de b" biçimiyle eşleşmesi gerektiği anlamına gelir

a -> b den morfizme f denir, sonra 'Belki a' dan morfizme -> 'Belki b' den 'fmap f' denir

Şimdi 'f' fonksiyonunun 'A' da ne yaptığını görelim ve onu 'B' de çoğaltabilecek miyiz

'A'da' f 'fonksiyon tanımı:

f :: a -> b

f alır ve b döndürür

'B' de 'f' fonksiyon tanımı:

f :: Maybe a -> Maybe b

f Belki a alır ve geri döner Belki b

'f' fonksiyonunu 'A' ile 'B' 'fmap f' fonksiyonuna eşlemek için fmap'in nasıl kullanılacağını görelim

fmap nedir?

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

Peki, burada ne yapıyoruz?

'A' türündeki 'x' işlevine 'f' işlevini uyguluyoruz. 'Nothing' özel kalıp eşleşmesi,Functor Maybe .

Böylece, nesnelerimizi [a, b] ve morfizmlerimizi [f] 'A' kategorisinden 'B' kategorisine eşledik.

Bu Functor!

resim açıklamasını buraya girin


İlginç bir cevap. Monad'larla tamamlamayı dilerim burritolar gibidir ( Soyutlama, sezgi ve “monad öğretici yanılgısı için komik cevap ) ve" Functor F her T tipini alır ve onu yeni bir FT tipine eşler " . Fonksiyonel Programlama ve Kategori Teorisi - Kategoriler ve Fonksiyonerler de faydalı oldu.
Ludovic Kuty

3

Genel Bakış

Fonksiyonel programlamada, bir functor aslında sıradan birliği kaldırma yapısıdır yeni tür değişkenler arasındaki fonksiyonlara fonksiyonların (yani bir argümanı olanların) . Düz nesneler arasında basit işlevler yazmak ve sürdürmek ve bunları kaldırmak için işlevler kullanmak, ardından karmaşık kap nesneleri arasında manuel olarak işlevler yazmak çok daha kolaydır. Diğer bir avantaj, düz işlevleri yalnızca bir kez yazmak ve daha sonra farklı işlevler aracılığıyla yeniden kullanmaktır.

Functorlara örnek olarak diziler, "belki" ve "ya" functorları, vadeli işlemler (bakınız örn. Https://github.com/Avaq/Fluture ) ve diğerleri dahildir.

örnekleme

Ad ve soyadından tam kişinin adını oluşturan işlevi göz önünde bulundurun. Bunu fullName(firstName, lastName)iki argümanın işlevi olarak tanımlayabiliriz , ancak bu yalnızca bir argümanın işlevleriyle ilgilenen işlevler için uygun olmaz. Çözümlemek için, tüm argümanları nameşimdi fonksiyonun tek argümanı olan tek bir nesnede topluyoruz :

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

Peki ya bir dizide çok sayıda insan varsa? Elle listenin üzerine gitmek yerine, biz sadece bizim işlevini yeniden kullanabilirsiniz fullNamearacılığı mapkod kısa tek bir satır Dizilerin için sağlanan yöntemle:

fullNameList = nameList => nameList.map(fullName)

ve onu gibi kullan

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

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

Biz bir aile odada her giriş olduğunda Yani, çalışacak nameListhem sağlayan bir nesnedir firstNameve lastNameözelliklerini. Ama ya bazı nesneler hiç nesne değilse (ya da hiç nesne değilse)? Hatalardan kaçınmak ve kodu daha güvenli hale getirmek için, nesnelerimizi Maybetüre sarabiliriz (ör. 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()

burada Just(name)yalnızca geçerli adları taşıyan bir konteyner ve Nothing()diğer her şey için kullanılan özel değerdir. Şimdi, argümanlarımızın geçerliliğini kontrol etmek için kesintiye uğratmak (veya unutmak) yerine, orijinal fullNamefonksiyonumuzu başka bir tek satırlık kodla tekrar kullanabiliriz (kaldırabiliriz) map, bu kez Belki de bu tür için sağlanan:

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

ve onu gibi kullan

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()

Kategori Teorisi

Bir Funktör içinde Kategori Teorisi kendi Morfizmlerin bileşimin saygı bu iki grup arasında bir haritasıdır. Bir de Bilgisayar Dili , ilgi ana kategorisi olan biridir nesneler olan tipleri olan (değerlerin belirli setleri) ve morfizimler olan fonksiyonları f:a->btüründen diğerine abaşka bir türe b.

Örneğin a, Stringtür, bSayı türü ve fbir dizeyi uzunluğuna eşleyen işlevdir:

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

Burada a = Stringtüm dizelerin b = Numberkümesini ve tüm sayı kümesini temsil eder . Bu anlamda, hem ave hem de Kategori Kategorisindekib nesneleri temsil eder (bu türler kategorisi ile yakından ilişkilidir, fark burada önemsizdir). Set Kategorisinde, iki set arasındaki morfizmler ilk setten ikinciye kadar tüm fonksiyonlardır. Buradaki uzunluk fonksiyonumuz , dizelerden sayı kümesine bir morfizmdir.f

Sadece belirlenmiş kategoriyi göz önüne aldığımızda, ilgili Functors kendi başına, belirli cebirsel yasaları karşılayan nesnelere ve morfizmlere nesne gönderen haritalardır.

Misal: Array

Arraybirçok şey anlamına gelebilir, ancak yalnızca bir şey bir Functor'dur - tür yapısı, bir türü tüm tür dizilerinin atürüyle eşleştirir . Örneğin, funktoru türünü eşler türü içine (istenilen uzunlukta dizeleri tüm diziler kümesi) ve kümesi türü tekabül türü içine (sayılar tüm diziler kümesi).[a]aArrayString[String]Number[Number]

Functor haritasını karıştırmamak önemlidir

Array :: a => [a]

bir morfizm ile a -> [a]. Funktor basitçe tipini (ortakları) eşler atürü içine [a]başka bir şey olarak. Her türün aslında bir dizi unsur olması, burada bir ilgisi yoktur. Aksine, bir morfizm bu kümeler arasındaki gerçek bir işlevdir. Örneğin, doğal bir morfizm (işlev) vardır

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

1 öğeli diziye bu değeri tek giriş olarak bir değer gönderir. Bu işlev Functor'ın bir parçası değildirArray ! Bu fonksiyonun bakış açısından pure, sadece diğerleri gibi bir fonksiyon, özel bir şey değil.

Öte yandan, ArrayFunctor'un ikinci kısmı - morfizm kısmı. Ki bu morfizmanın eşler f :: a -> bbir morfizma içine [f] :: [a] -> [b]:

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

Burada arrtip değerleri ile istenilen uzunlukta bir dizidir ave arr.map(f)tip değerleriyle aynı uzunlukta dizi bgirişleri uygulamak aşağıdaki sonuçları, fgirişlere arr. Bunu bir işlev haline getirmek için, kimliği kimliğe ve kompozisyonları kompozisyonlara eşlemenin matematiksel yasaları bu Arrayörnekte kontrol edilmesi kolay olmalıdır .


2

Önceki teorik veya matematiksel cevaplarla çelişmemek için değil, Functor aynı zamanda sadece bir yöntemi olan ve etkin bir şekilde işlev olarak kullanılan bir Nesnedir (Nesneye Dayalı programlama dilinde).

Bir örnek Java'da sadece bir yöntemi olan Runnable arabirimidir: run.

İlk önce birinci sınıf işlevleri olan Javascript'te bu örneği ele alalım:

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

Çıktı: [1, 4, 25, 100]

Map yöntemi bir işlev alır ve her öğenin o dizinin orijinal dizideki aynı konumdaki değere uygulanmasının sonucu olduğu yeni bir dizi döndürür.

Aynı şeyi yapmak için Java, bir Functor kullanarak, önce bir arayüz tanımlamanız gerekir, örneğin:

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

Ardından, harita işlevi olan bir koleksiyon sınıfı eklerseniz, şunları yapabilirsiniz:

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

Bu, önceki JavaScript örneğindeki işlevin OO eşdeğeri olan bir Functor oluşturmak için IntMapFunction'un satır içi alt sınıfını kullanır.

Functor'ları kullanmak bir OO dilinde işlevsel teknikleri uygulamanızı sağlar. Tabii ki, bazı OO dilleri de doğrudan işlevleri destekliyor, bu yüzden bu gerekli değil.

Referans: http://en.wikipedia.org/wiki/Function_object


Aslında "fonksiyon nesnesi" bir fonksiyonlayıcının doğru bir tanımı değildir. Örn Array, bir işlevcidir, ancak Array(value)yalnızca 1 elemanlı diziler verir.
Dmitri Zaitsev

0

ÖPÜCÜK: Bir işlev bir eşleme yöntemi olan bir nesnedir.

JavaScript'teki diziler haritayı uygular ve bu nedenle işlevlidir. Vaatler, Akarsu ve Ağaçlar genellikle fonksiyonel dillerde harita uygular ve yaptıklarında functor olarak kabul edilirler. Functor'un map yöntemi kendi içeriğini alır ve haritaya aktarılan dönüşüm geri aramasını kullanarak her birini dönüştürür ve yapıyı ilk functor olarak içeren, ancak dönüştürülmüş değerlerle içeren yeni bir functor döndürür.

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


1
Yan not ile 'nesne' çok geniş bir şekilde ele alınmalı ve sadece 'bir şey' anlamına gelir. OOP-diller için, örneğin, yerine nesne için sınıfın . Bir 'functor, Functor arayüzünü uygulayan bir sınıftır' diyebilir (Elbette, bu arayüz fiziksel olarak orada olmayabilir, ancak 'harita' mantığını bu arayüze kaldırabilir ve tüm eşlenebilir sınıflarınızın paylaşmasını sağlayabilirsiniz. - yazı sisteminiz bu genel şeyleri yazmaya izin verdiği müddetçe, yani).
Qqwy

1
Dürüst olmak gerekirse süper kafa karıştırıcı Sınıflar buluyorum, bir tarafta sadece somut bir şey için bir plan / ama aynı zamanda yöntemleri (statik şeyler) olabilir ve nesneler gibi davranabilirler. Sınıf, oluşturduğu arabirimi veya örneği uygular mı?
soundyogi

1
Evet, kafa karıştırıcı olabilirler. Ama: Sınıflar uygulamak arabirimleri (arabirimler yöntemlerinde verildi boşlukları kişilermişiz 'dolguyu' Başka bir deyişle:. Onlar (örneği) kelime oyununu bağışlayın anında olabilir somut kılavuzda arayüzünün soyut kurallar dönüş). 'Sınıflar nesne gibi davranırlar': Ruby gibi gerçekten OOP dillerinde, Sınıflar 'Sınıf' Sınıfının örnekleridir. Tamamen kaplumbağalar.
Qqwy

Arraytype construct tek bir işlevi tanımlar. Örneklerine "diziler" de denir, ancak bunlar işlev oluşturmaz. Buradaki açıklama daha kesin yapılmalıdır.
Dmitri Zaitsev

@DmitriZaitsev Ayrıntı verebilir misiniz? Yani söylediğiniz şey, örneklerin functor olmadığıdır? Birini eşleştirerek yeni bir işlev elde ettiğiniz için bu konuda bir şey görmüyorum.
soundyogi

-4

Pratikte, functor, C ++ 'da çağrı operatörünü uygulayan bir nesne anlamına gelir. Ocaml'de functor'un bir modülü girdi olarak alan ve başka bir modülü çıkaran bir şeyi ifade ettiğini düşünüyorum.


-6

Basitçe söylemek gerekirse, bir işlev veya işlev nesnesi, tıpkı bir işlev gibi çağrılabilen bir sınıf nesnesidir.

C ++ dilinde:

Bir işlevi böyle yazarsınız

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

Bu şekilde bir functor yazarsınız

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

Şimdi bunu yapabilirsiniz:

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

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

Bunları bu kadar harika yapan şey, sınıfta durumu koruyabilmenizdir - bir fonksiyona kaç kez çağrıldığını sormak isteyip istemediğinizi düşünün. Bunu düzgün, kapsüllenmiş bir şekilde yapmanın bir yolu yok. Bir işlev nesnesiyle, tıpkı diğer herhangi bir sınıfa benzer: arttığınız bazı örnek değişkeniniz operator ()ve bu değişkeni denetlemek için bir yönteminiz olacak ve istediğiniz her şey düzgün.


12
Hayır, bu işlevler FP dilleri tarafından kullanılan tip teorisi kavramı değildir.
Tobu

1
FunctorClassBirinci Functor Yasası'nı yerine getirdiğini nasıl kanıtlayabildiğini görebilirim , ancak ikinci Yasa için bir kanıt çizebilir misiniz? Tam olarak görmüyorum.
Jörg W Mittag

3
Bah, haklısınız. "Web son derece teknik açıklamalar sağladı" çözme bir sapma aldı ve aşırı önlemek için, "ML dil dil ailesinde bir functor parametre olarak bir veya daha fazla modül alır" bir modül. Ancak bu cevap kötüdür. Aşırı basitleştirilmiş ve yetersiz. Ragedelete için cazip geliyorum, ama gelecek nesiller için kafalarını sallamak için bırakacağım :)
Matt

Cevabı ve yorumları bıraktığınıza sevindim, çünkü sorunu çerçevelemeye yardımcı oluyor. Teşekkür ederim! Cevapların çoğunun Haskell veya OCaml cinsinden yazılması konusunda sorun yaşıyorum ve bana göre timsahları timsah olarak açıklamak gibi.
Rob

-10

Functor özel olarak fonksiyonel programlama ile ilgili değildir. Bir işleve ya da bir tür nesneye bir "işaretçi" dir, bu işlev olarak işlev olarak adlandırılabilir.


8
Bir functor (kategori teorisinden) için belirli bir FP kavramı vardır, ancak aynı kelimenin FP dışı dillerde başka şeyler için de kullanıldığından emin olabilirsiniz.
Craig Stuntz

İşlev işaretçilerin Functor olduğundan emin misiniz? Fonksiyon göstergelerinin iki Functor Yasasını, özellikle İkinci Functor Yasasını (morfizm kompozisyonunun korunması) nasıl yerine getirdiğini görmüyorum. Bunun için bir kanıtın var mı? (Sadece kaba bir taslak.)
Jörg W Mittag
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.