Sök…


Introduktion

Functor är den klass av typerna f :: * -> * som kan kartläggas överensstämmigt. Kartläggning av en funktion över en datastruktur tillämpar funktionen på alla element i strukturen utan att ändra strukturen själv.

Anmärkningar

En Functor kan betraktas som en behållare för något värde, eller ett beräkningssammanhang. Exempel är Maybe a eller [a] . Typeclassopedia- artikeln har en bra skrivning av begreppen bakom Functors.

För att betraktas som en riktig Functor måste en instans respektera de två följande lagarna:

Identitet

fmap id == id

Sammansättning

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

Vanliga instanser av Functor

Kanske

Maybe är en Functor innehåller ett eventuellt frånvarande värde:

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

Maybe fallet av Functor tillämpar en funktion på ett värde som är inslaget i en Just . Om beräkningen tidigare har misslyckats (så Maybe värdet är Nothing ), finns det inget värde att använda funktionen på, så fmap är ett no-op.

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

Vi kan kontrollera funktorns lagar för det här fallet med hjälp av ekvationella resonemang. För identitetslagen,

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

För kompositionslagen,

(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

listor

Listas instans av Functor tillämpar funktionen för varje värde i listan på plats.

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

Detta kan alternativt skrivas som en fmap f xs = [fx | x <- xs] : fmap f xs = [fx | x <- xs] .

Detta exempel visar att fmap generaliserar map . map fungerar endast på listor, medan fmap fungerar på en godtycklig Functor .

Identitetslagstiftningen kan visas att hålla genom induktion:

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

och på liknande sätt kompositionslagen:

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

funktioner

Inte varje Functor ser ut som en container. Funktioners instans av Functor tillämpar en funktion på returvärdet för en annan funktion.

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

Observera att denna definition motsvarar fmap = (.) . Så fmap generaliserar funktionskomposition.

Kontrollera igen identitetslagen:

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

och kompositionslagen:

(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

Klassdefinition av funktor och lagar

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

Ett sätt att titta på det är att fmap lyfter en funktion av värden till en funktion av värden i ett sammanhang f .

En korrekt instans av Functor bör tillfredsställa funktorlagarna , även om dessa inte verkställs av kompilatorn:

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

Det finns ett vanligt använt infixalias för fmap heter <$> .

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

Ersätt alla element i en Functor med ett enda värde

Data.Functor modulen innehåller två kombinationer, <$ och $> , som ignorerar alla värden i en funktor och ersätter dem alla med ett enda konstant värde.

infixl 4 <$, $>

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

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

void ignorerar en beräknings returvärde.

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

Polynomiska funktorer

Det finns en användbar uppsättning Functor för att bygga stora Functor av mindre. Dessa är lärorika som exempel på Functor , och de är också användbara som en teknik för generisk programmering, eftersom de kan användas för att representera en stor klass av vanliga funktorer.

Identitetsfunktorn

Identitetsfunktorn släpper helt enkelt upp sitt argument. Det är en typnivåimplementering av I kombinatorn från SKI-kalkylen.

newtype I a = I a

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

I kan hittas under namnet Identity i Data.Functor.Identity modulen .

Den ständiga funktorn

Den ständiga funktorn ignorerar sitt andra argument och innehåller endast ett konstant värde. Det är en typnivåanalog av const , K kombinatorn från SKI-kalkylen.

newtype K c a = K c

Notera att K ca inte innehåller några a -värden; K () är isomorf för Proxy . Detta innebär att K : s implementering av fmap inte gör någon kartläggning alls!

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

K är annars känd som Const , från Data.Functor.Const .

De återstående funktorerna i detta exempel kombinerar mindre funktorer till större.

Functor produkter

Funktorprodukten tar ett par funktorer och förpackar dem. Det är analogt med en tupel, förutom att medan (,) :: * -> * -> * fungerar på types * , (:*:) :: (* -> *) -> (* -> *) -> (* -> *) arbetar med 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

Denna typ finns under namnet Product i Data.Functor.Product modulen .

Funktorkoprodukter

Precis som :*: är analog med (,) :+: är funktornivån analog till 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)

:+: kan hittas under namnet Sum i Data.Functor.Sum modulen .

Funktorsammansättning

Slutligen :.: Fungerar som en typnivå (.) , Tar utgången från en funktor och rör den i ingången till en annan.

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)

Compose finns i Data.Functor.Compose

Polynomiska funktorer för generisk programmering

I , K :*: , :+: och :.: Kan betraktas som ett byggsätt för en viss klass enkla datatyper. Satsen blir särskilt kraftfull när du kombinerar den med fasta punkter eftersom datatyper byggda med dessa kombinatorer automatiskt är instanser av Functor . Du använder satsen för att bygga en malltyp, markera rekursiva punkter med hjälp av I och sedan ansluta den till Fix att få en typ som kan användas med standardzoo för rekursionsscheman.

namn Som en datatyp Använda funktorsatsen
Värden par data Pair a = Pair aa type Pair = I :*: I
Två för två rutnät type Grid a = Pair (Pair a) type Grid = Pair :.: Pair
Naturliga siffror data Nat = Zero | Succ Nat type Nat = Fix (K () :+: I)
listor data List a = Nil | Cons a (List a) type List a = Fix (K () :+: K a :*: I)
Binära träd data Tree a = Leaf | Node (Tree a) a (Tree a) type Tree a = Fix (K () :+: I :*: K a :*: I)
Roseträd data Rose a = Rose a (List (Rose a)) type Rose a = Fix (K a :*: List :.: I)

Denna "kit" -metod för att designa datatyper är idén bakom generiska programmeringsbibliotek som generics-sop . Tanken är att skriva generiska operationer med hjälp av ett kit som det som presenterats ovan och sedan använda en typklass för att konvertera godtyckliga datatyper till och från deras generiska representation:

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

Funktorer i kategoriteori

En funktor definieras i kategoriteori som en strukturbevarande karta (en 'homomorfism') mellan kategorierna. Specifikt mappas (alla) objekt till objekt och (alla) pilar mappas till pilar, så att kategorilagarna bevaras.

Den kategori där objekt är Haskell-typer och morfismer är Haskell-funktioner kallas Hask . Så en funktor från Hask till Hask skulle bestå av en kartläggning av typer till typer och en kartläggning från funktioner till funktioner.

Förhållandet som det här kategoriteoretiska konceptet har till Haskell-programmeringskonstruktionen Functor är ganska direkt. Kartläggningen från typer till typer har formen av en typ f :: * -> * , och kartläggningen från funktioner till funktioner har formen av en funktion fmap :: (a -> b) -> (fa -> fb) . Sätta samman dem i en klass,

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

fmap är en operation som tar en funktion (en typ av morfism), :: a -> b , och kartlägger den till en annan funktion, :: fa -> fb . Det antas (men lämnas till programmeraren för att säkerställa) att instanser av Functor verkligen är matematiska funktorer, vilket bevarar Hasks kategoriska struktur:

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

fmap lyfter en funktion :: a -> b in i en underkategori av Hask på ett sätt som bevarar både existensen av alla identitetspilar och kompositionens associativitet.


Functor klassen kodar endast för endo- funktorer på Hask . Men i matematik kan funktorer kartlägga mellan godtyckliga kategorier. En mer trogen kodning av detta koncept skulle se ut så här:

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)

Functor-klassen är ett speciellt fall i denna klass där källkategorin och målkategorierna båda är Hask . Till exempel,

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

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

Härleda Functor

DeriveFunctor språkförlängningen tillåter GHC att generera instanser av Functor automatiskt.

{-# 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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow