Zoeken…


Invoering

Functor is de klasse van typen f :: * -> * die covariant in kaart kan worden gebracht . Een functie toewijzen aan een gegevensstructuur past de functie toe op alle elementen van de structuur zonder de structuur zelf te wijzigen.

Opmerkingen

Een Functor kan worden gezien als een container voor een bepaalde waarde, of een berekeningscontext. Voorbeelden zijn Maybe a of [a] . Het artikel Typeclassopedia heeft een goede beschrijving van de concepten achter Functors.

Om als een echte Functor te worden beschouwd, moet een instantie de 2 volgende wetten respecteren:

Identiteit

fmap id == id

Samenstelling

fmap (f . g) = (fmap f) . (fmap g)

Veel voorkomende voorbeelden van Functor

Kan zijn

Maybe is een Functor die een mogelijk afwezige waarde bevat:

instance Functor Maybe where
    fmap f Nothing = Nothing
    fmap f (Just x) = Just (f x)

Maybe past het exemplaar van Functor een functie toe op een waarde gewikkeld in een Just . Als de berekening eerder is mislukt (dus de waarde Maybe is Nothing ), dan is er geen waarde waarop de functie kan worden toegepast, dus fmap is een no-op.

> fmap (+ 3) (Just 3)
Just 6
> fmap length (Just "mousetrap")
Just 9
> fmap sqrt Nothing
Nothing

We kunnen de functorwetten voor dit geval controleren met behulp van equationeel redeneren. Voor de identiteitswet,

fmap id Nothing
Nothing  -- definition of fmap
id Nothing  -- definition of id

fmap id (Just x)
Just (id x)  -- definition of fmap
Just x  -- definition of id
id (Just x)  -- definition of id

Voor de compositiewet,

(fmap f . fmap g) Nothing
fmap f (fmap g Nothing)  -- definition of (.)
fmap f Nothing  -- definition of fmap
Nothing  -- definition of fmap
fmap (f . g) Nothing  -- because Nothing = fmap f Nothing, for all f

(fmap f . fmap g) (Just x)
fmap f (fmap g (Just x))  -- definition of (.)
fmap f (Just (g x))  -- definition of fmap
Just (f (g x))  -- definition of fmap
Just ((f . g) x)  -- definition of (.)
fmap (f . g) (Just x)  -- definition of fmap

lijsten

Lists instantie van Functor past de functie toe op elke waarde in de lijst.

instance Functor [] where
    fmap f [] = []
    fmap f (x:xs) = f x : fmap f xs

Dit kan ook worden geschreven als een fmap f xs = [fx | x <- xs] : fmap f xs = [fx | x <- xs] .

Dit voorbeeld laat zien dat fmap map generaliseert. map werkt alleen op lijsten, terwijl fmap op een willekeurige Functor .

De identiteitswet kan worden aangetoond door inductie:

-- base case
fmap id []
[]  -- definition of fmap
id []  -- definition of id

-- inductive step
fmap id (x:xs)
id x : fmap id xs  -- definition of fmap
x : fmap id xs  -- definition of id
x : id xs  -- by the inductive hypothesis
x : xs  -- definition of id
id (x : xs)  -- definition of id

en op dezelfde manier: de compositiewet:

-- base case
(fmap f . fmap g) []
fmap f (fmap g [])  -- definition of (.)
fmap f []  -- definition of fmap
[]  -- definition of fmap
fmap (f . g) []  -- because [] = fmap f [], for all f

-- inductive step
(fmap f . fmap g) (x:xs)
fmap f (fmap g (x:xs))  -- definition of (.)
fmap f (g x : fmap g xs)  -- definition of fmap
f (g x) : fmap f (fmap g xs)  -- definition of fmap
(f . g) x : fmap f (fmap g xs)  -- definition of (.)
(f . g) x : fmap (f . g) xs  -- by the inductive hypothesis
fmap (f . g) xs  -- definition of fmap

functies

Niet elke Functor ziet eruit als een container. Functies 'instantie van Functor past een functie toe op de retourwaarde van een andere functie.

instance Functor ((->) r) where
    fmap f g = \x -> f (g x)

Merk op dat deze definitie equivalent is aan fmap = (.) . Dus fmap generaliseert functiesamenstelling.

Nogmaals het controleren van de identiteitswet:

fmap id g
\x -> id (g x)  -- definition of fmap
\x -> g x  -- definition of id
g  -- eta-reduction
id g  -- definition of id

en de compositiewet:

(fmap f . fmap g) h
fmap f (fmap g h)  -- definition of (.)
fmap f (\x -> g (h x))  -- definition of fmap
\y -> f ((\x -> g (h x)) y)  -- definition of fmap
\y -> f (g (h y))  -- beta-reduction
\y -> (f . g) (h y)  -- definition of (.)
fmap (f . g) h  -- definition of fmap

Klasse Definitie van Functor en Wetten

class Functor f where
    fmap :: (a -> b) -> f a -> f b

Een manier om ernaar te kijken is dat fmap een functie van waarden opheft in een functie van waarden in een context f .

Een correct exemplaar van Functor moet voldoen aan de functorwetten , hoewel deze niet worden afgedwongen door de compiler:

fmap id = id                    -- identity
fmap f . fmap g = fmap (f . g)  -- composition

Er is een algemeen gebruikte infix-alias voor fmap naam <$> .

infixl 4 <$>
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap

Alle elementen van een Functor vervangen door een enkele waarde

De Data.Functor module bevat twee combinators, <$ en $> , die alle waarden in een functor negeren en ze allemaal vervangen door een enkele constante waarde.

infixl 4 <$, $>

<$ :: Functor f => a -> f b -> f a
(<$) = fmap . const

$> :: Functor f => f a -> b -> f b
($>) = flip (<$)

void negeert de retourwaarde van een berekening.

void :: Functor f => f a -> f ()
void = (() <$)

Polynomiale functoren

Er is een handige set Functor voor het bouwen van grote Functor uit kleinere. Deze zijn leerzaam als voorbeeldinstanties van Functor en ze zijn ook nuttig als een techniek voor generieke programmering, omdat ze kunnen worden gebruikt om een grote klasse gemeenschappelijke functors te vertegenwoordigen.

De identiteitsfunctor

De identiteitsfunctor sluit eenvoudigweg zijn argumentatie af. Het is een type-niveau implementatie van de I combinator van SKI calculus.

newtype I a = I a

instance Functor I where
    fmap f (I x) = I (f x)

I te vinden onder de naam Identity in de module Data.Functor.Identity .

De constante functor

De constante functor negeert zijn tweede argument, dat alleen een constante waarde bevat. Het is een type-niveau-analoog van const , de K combinator uit SKI calculus.

newtype K c a = K c

Merk op dat K ca geen bevat a -waarden; K () is isomorf voor Proxy . Dit betekent dat K 's implementatie van fmap helemaal geen mapping fmap !

instance Functor (K c) where
    fmap _ (K c) = K c

K is ook bekend als Const , van Data.Functor.Const .

De overige functoren in dit voorbeeld combineren kleinere functoren in grotere.

Functor-producten

Het functorproduct neemt een paar functors en pakt ze in. Het is analoog aan een tuple, behalve dat terwijl (,) :: * -> * -> * werkt op types * , (:*:) :: (* -> *) -> (* -> *) -> (* -> *) werkt op functors * -> * .

infixl 7 :*:
data (f :*: g) a = f a :*: g a

instance (Functor f, Functor g) => Functor (f :*: g) where
    fmap f (fx :*: gy) = fmap f fx :*: fmap f gy

Dit type is te vinden onder de naam Product in de module Data.Functor.Product .

Functor coproducts

Net als :*: is analoog aan (,) ,: :+: is het functieniveau-analoog van Either .

infixl 6 :+:
data (f :+: g) a = InL (f a) | InR (g a)

instance (Functor f, Functor g) => Functor (f :+: g) where
    fmap f (InL fx) = InL (fmap f fx)
    fmap f (InR gy) = InR (fmap f gy)

:+: is te vinden onder de naam Sum , in de module Data.Functor.Sum .

Functor samenstelling

Ten slotte :.: Werkt als een type-niveau (.) , Waarbij de uitvoer van de ene functor wordt overgenomen en naar de invoer van een andere wordt geleid.

infixr 9 :.:
newtype (f :.: g) a = Cmp (f (g a))

instance (Functor f, Functor g) => Functor (f :.: g) where
    fmap f (Cmp fgx) = Cmp (fmap (fmap f) fgx)

Het Compose type is te vinden in Data.Functor.Compose

Polynomiale functoren voor generieke programmering

I , K :*: , :+: en :.: Kan worden gezien als een pakket bouwstenen voor een bepaalde klasse eenvoudige gegevenstypen. De kit wordt vooral krachtig wanneer u deze combineert met vaste punten, omdat gegevenstypen die met deze combinators zijn gebouwd, automatisch exemplaren van Functor . U gebruikt de kit om een sjabloontype te bouwen, recursieve punten te markeren met I en vervolgens in Fix te Fix om een type te krijgen dat kan worden gebruikt met de standaard dierentuin van recursieschema's.

Naam Als een gegevenstype Gebruik van de functiekit
Paren van waarden data Pair a = Pair aa type Pair = I :*: I
Twee bij twee rasters type Grid a = Pair (Pair a) type Grid = Pair :.: Pair
Natuurlijke getallen data Nat = Zero | Succ Nat type Nat = Fix (K () :+: I)
lijsten data List a = Nil | Cons a (List a) type List a = Fix (K () :+: K a :*: I)
Binaire bomen data Tree a = Leaf | Node (Tree a) a (Tree a) type Tree a = Fix (K () :+: I :*: K a :*: I)
Rozenbomen data Rose a = Rose a (List (Rose a)) type Rose a = Fix (K a :*: List :.: I)

Deze "kit" -benadering voor het ontwerpen van gegevenstypen is het idee achter generieke programmeerbibliotheken zoals generics-sop . Het idee is om generieke bewerkingen te schrijven met behulp van een kit zoals hierboven gepresenteerd, en vervolgens een typeklasse te gebruiken om willekeurige gegevenstypen om te zetten naar en van hun generieke weergave:

class Generic a where
    type Rep a  -- a generic representation built using a kit
    to :: a -> Rep a
    from :: Rep a -> a

Functors in Category Theory

Een Functor wordt in de categorietheorie gedefinieerd als een structuurbehoudende kaart (een 'homomorfisme') tussen categorieën. In het bijzonder worden (alle) objecten toegewezen aan objecten, en (alle) pijlen worden toegewezen aan pijlen, zodat de categoriewetten behouden blijven.

De categorie waarin objecten Haskell-typen zijn en morfismen Haskell-functies zijn, wordt Hask genoemd . Een functor van Hask naar Hask zou dus bestaan uit een toewijzing van typen aan typen en een toewijzing van functies aan functies.

De relatie die dit categorietheoretische concept heeft met het Haskell-programmeerconstruct Functor is vrij direct. De toewijzing van typen naar typen neemt de vorm aan van een type f :: * -> * , en de toewijzing van functies aan functies neemt de vorm aan van een functie fmap :: (a -> b) -> (fa -> fb) . Die samenbrengen in een klas,

class Functor (f :: * -> *) where
    fmap :: (a -> b) -> f a -> f b

fmap is een bewerking die een functie (een soort morfisme) gebruikt, :: a -> b , en deze toewijst aan een andere functie, :: fa -> fb . Er wordt aangenomen (maar aan de programmeur overgelaten om ervoor te zorgen) dat instanties van Functor inderdaad wiskundige functors zijn, met behoud van de categorische structuur van Hask :

fmap (id {- :: a -> a -})  ==  id {- :: f a -> f a -}
fmap (h . g)               ==  fmap h . fmap g

fmap tilt een functie :: a -> b in een subcategorie van Hask op een manier die zowel het bestaan van identiteitspijlen als de associativiteit van de compositie behoudt.


De Functor klasse alleen codeert Endo functors op Hask. Maar in de wiskunde kunnen functoren in kaart brengen tussen willekeurige categorieën. Een meer getrouwe codering van dit concept zou er zo uitzien:

class Category c where
    id  :: c i i
    (.) :: c j k -> c i j -> c i k

class (Category c1, Category c2) => CFunctor c1 c2 f where
    cfmap :: c1 a b -> c2 (f a) (f b)

De standaard Functor-klasse is een speciaal geval van deze klasse waarin de bron- en doelcategorie Hask zijn . Bijvoorbeeld,

instance Category (->) where        -- Hask
    id    = \x -> x
    f . g = \x -> f (g x)

instance CFunctor (->) (->) [] where
    cfmap = fmap

Afgeleide functor

Met de DeriveFunctor kan GHC automatisch instanties van Functor genereren.

{-# LANGUAGE DeriveFunctor #-}

data List a = Nil | Cons a (List a) deriving Functor

-- instance Functor List where            -- automatically defined
--   fmap f Nil = Nil
--   fmap f (Cons x xs) = Cons (f x) (fmap f xs)

map :: (a -> b) -> List a -> List b
map = fmap


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow