J'ai accidentellement écrit un
Haskell Foncteurs Tutoriel
Je vais répondre à votre question à l'aide d'exemples, et je vais mettre le types en dessous dans les commentaires.
Watch out pour le modèle dans les types de.
fmap
est une généralisation de l' map
Les foncteurs sont pour vous donner l' fmap
fonction. fmap
fonctionne comme map
, de sorte que nous allons vérifier map
première:
map (subtract 1) [2,4,8,16] = [1,3,7,15]
-- Int->Int [Int] [Int]
Elle utilise donc la fonction (subtract 1)
à l'intérieur de la liste. En fait, pour les listes, fmap
fait exactement ce qu' map
n'. Nous allons multiplier le tout par 10 cette fois:
fmap (* 10) [2,4,8,16] = [10,40,80,160]
-- Int->Int [Int] [Int]
Je décrirais ce que la cartographie de la fonction qui multiplie par 10 sur la liste.
fmap
travaille également sur l' Maybe
Quoi d'autre puis-je fmap
- dessus? Nous allons Peut-être utiliser le type de données, qui dispose de deux types de valeurs, Nothing
et Just x
. (Vous pouvez utiliser Nothing
pour représenter un échec à obtenir une réponse en Just x
représente une réponse.)
fmap (+7) (Just 10) = Just 17
fmap (+7) Nothing = Nothing
-- Int->Int Maybe Int Maybe Int
OK, donc encore une fois, fmap
est à l'aide de (+7)
à l'intérieur de la Peut-être.
Et nous pouvons fmap autres fonctions. length
trouve la longueur d'une liste, de sorte que nous pouvons fmap sur Maybe [Double]
fmap length Nothing = Nothing
fmap length (Just [5.0, 4.0, 3.0, 2.0, 1.573458]) = Just 5
-- [Double]->Int Maybe [Double] Maybe Int
En fait length :: [a] -> Int
mais je l'utilise ici - [Double]
je me suis donc spécialisé elle.
Nous allons utiliser show
tourner des trucs dans des chaînes de caractères. Secrètement le type réel de l' show
est Show a => a -> String
, mais c'est un peu long, et je l'emploie ici sur un Int
, de sorte qu'il est spécialisé pour Int -> String
.
fmap show (Just 12) = Just "12"
fmap show Nothing = Nothing
-- Int->String Maybe Int Maybe String
aussi, en regardant en arrière à des listes
fmap show [3,4,5] = ["3", "4", "5"]
-- Int->String [Int] [String]
fmap
fonctionne sur Either something
Nous allons l'utiliser sur une structure légèrement différente, Either
. Les valeurs de type Either a b
sont soit Left a
valeurs ou Right b
valeurs. Parfois, nous utiliser pour représenter un succès Right goodvalue
ou de l'échec Left errordetails
, et parfois juste pour mélanger les valeurs de deux types dans une. De toute façon, le foncteur pour le type de données ne fonctionne que sur l' Right
- il des feuilles Left
uniquement des valeurs. Qui a du sens, surtout si vous êtes en utilisant les valeurs de Droite que ceux qui réussissent (et, en fait, nous ne serions pas en mesure de le faire fonctionner sur les deux parce que les types ne sont pas nécessairement les mêmes). Permet d'utiliser le type Either String Int
comme un exemple
fmap (5*) (Left "hi") = Left "hi"
fmap (5*) (Right 4) = Right 20
-- Int->Int Either String Int Either String Int
Il fait (5*)
travail à l'intérieur de l'un ou l'autre, mais pour Eithers, seulement l' Right
des valeurs sont modifiées. Mais nous pouvons le faire dans l'autre sens sur Either Int String
, tant que la fonction travaille sur des chaînes de caractères. Mettons ", cool!"
à la fin de trucs, à l'aide de (++ ", cool!")
.
fmap (++ ", cool!") (Left 4) = Left 4
fmap (++ ", cool!") (Right "fmap edits values") = Right "fmap edits values, cool!"
-- String->String Either Int String Either Int String
C'est cool surtout pour utiliser fmap
sur IO
Maintenant, une de mes préférées des façons d'utiliser fmap est de l'utiliser sur l' IO
valeurs à modifier la valeur de certains IO opération me donne. Nous allons prendre un exemple qui vous permet de taper quelque chose et puis l'imprime tout de suite:
echo1 :: IO ()
echo1 = do
putStrLn "Say something!"
whattheysaid <- getLine -- getLine :: IO String
putStrLn whattheysaid -- putStrLn :: String -> IO ()
On peut écrire que dans une manière qui se sent plus propre à moi:
echo2 :: IO ()
echo2 = putStrLn "Say something"
>> getLine >>= putStrLn
>>
ne fait qu'une chose après l'autre, mais la raison pour laquelle j'aime c'est parce qu' >>=
prend la Chaîne getLine
nous a donné et il se nourrissait de putStrLn
qui prend une Chaîne de caractères.
Que faire si nous voulions saluer l'utilisateur:
greet1 :: IO ()
greet1 = do
putStrLn "What's your name?"
name <- getLine
putStrLn ("Hello, " ++ name)
Si nous voulions écrire que dans la plus propre façon je suis un peu coincé. J'aurais du écrire
greet2 :: IO ()
greet2 = putStrLn "What's your name?"
>> getLine >>= (\name -> putStrLn ("Hello, " ++ name))
qui est pas plus agréable que la do
version. En fait, l' do
de la notation est-il de sorte que vous n'avez pas à le faire. Mais peut - fmap
venir à la rescousse? Oui, il peut. ("Hello, "++)
est une fonction que je peux fmap sur le getLine!
fmap ("Hello, " ++) getLine = -- read a line, return "Hello, " in front of it
-- String->String IO String IO String
nous pouvons l'utiliser comme ceci:
greet3 :: IO ()
greet3 = putStrLn "What's your name?"
>> fmap ("Hello, "++) getLine >>= putStrLn
On peut tirer de ce truc sur quoi que ce soit que nous sommes donné. Nous allons être en désaccord avec le fait que "Vrai" ou "Faux" a été tapé dans:
fmap not readLn = -- read a line that has a Bool on it, change it
-- Bool->Bool IO Bool IO Bool
Ou disons juste rapport de la taille d'un fichier:
fmap length (readFile "test.txt") = -- read the file, return its length
-- String->Int IO String IO Int
-- [a]->Int IO [Char] IO Int (more precisely)
Conclusions: Qu'est - fmap
le faire, et que faut-il faire pour?
Si vous avez été en regardant les modèles dans les types et la réflexion sur les exemples que vous aurez remarqué que les fmap prend une fonction qui fonctionne sur certaines valeurs, et applique cette fonction sur quelque chose qu'on a ou le produit de ces valeurs, en quelque sorte, modifier les valeurs. (par exemple readLn était de lire Bool, il a donc un type IO Bool
il y a une valeur Booléenne dans le sens qu'elle produit un Bool
, eg2 [4,5,6]
a Int
s en elle.)
fmap :: (a -> b) -> Something a -> Something b
cela fonctionne pour avoir quelque Chose de Liste de la (écrites []
), Maybe
, Either String
, Either Int
, IO
et des charges de plus de choses. Nous l'appelons un Foncteur si cela fonctionne d'une manière sensible (il y a quelques règles - plus tard). Le type réel de fmap est
fmap :: Functor something => (a -> b) -> something a -> something b
mais nous avons l'habitude de remplacer something
avec f
pour des raisons de concision. Il est tout de même à le compilateur, si:
fmap :: Functor f => (a -> b) -> f a -> f b
Avoir un coup d'oeil sur les types et vérifier cela fonctionne toujours - chose à propos de Either String Int
attentivement - ce que l' f
ce moment-là?
Annexe: Quels sont le Foncteur règles, et pourquoi en avons-nous?
id
est la fonction identité:
id :: a -> a
id x = x
Voici les règles:
fmap id == id -- identity identity
fmap (f . g) == fmap f . fmap g -- composition
Tout d'abord l'identité de l'identité: Si vous avez la carte la fonction qui ne fait rien, ça ne change rien. Cela semble évident (un grand nombre de règles), mais vous pouvez l'interpréter comme disant qu' fmap
est uniquement autorisé à modifier les valeurs, et non la structure. fmap
n'est pas autorisé à tourner Just 4
en Nothing
ou [6]
en [1,2,3,6]
ou Right 4
en Left 4
parce que plus que la modification des données de la structure ou de contexte pour que les données modifiées.
J'ai touché à cette règle une fois quand j'ai travaillé sur une interface utilisateur graphique du projet - je voulais être en mesure de modifier les valeurs, mais je ne pouvais pas le faire sans changer la structure sous-jacente. Nul n'aurait vraiment remarqué la différence, car il a eu le même effet, mais la réalisation, il n'a pas obéir à l'foncteur règles m'a fait repenser toute ma conception, et c'est beaucoup plus propre, plus lisse et plus rapide maintenant.
Deuxièmement, la composition: cela signifie que vous pouvez choisir de fmap une seule fonction à la fois, ou fmap les deux en même temps. Si fmap
quitte la structure/cadre de vos valeurs et modifie simplement avec la fonction de son, il sera de travailler avec cette règle trop.
Pourquoi en avons-nous? Assurez - fmap
n'est pas sournoisement faire quoi que ce soit derrière les scènes ou de changer quoi que ce soit nous ne nous attendions pas. Ils ne sont pas appliquées par le compilateur (demander au compilateur de prouver un théorème avant qu'il compile ton code n'est pas juste, et permettrait de ralentir compilation - le programmeur doit vérifier). Cela signifie que vous pouvez tricher, mais c'est un mauvais plan parce que votre code peut donner des résultats inattendus.
3 votes
J'ai trouvé le billet de blog de Gabriel, Le schéma de conception du foncteur, plutôt bon. Ce n'est pas exactement de l'anglais simple, mais vous devriez le lire et voir si cela vous aide.
0 votes
Lien d'Anton Guryanov : en.wikibooks.org/wiki/Haskell/Category_theory