Quelle est la différence entre le point (.)
et le symbole du dollar ($)
?
D'après ce que je comprends, il s'agit dans les deux cas d'un sucre syntaxique permettant de ne pas utiliser de parenthèses.
Quelle est la différence entre le point (.)
et le symbole du dollar ($)
?
D'après ce que je comprends, il s'agit dans les deux cas d'un sucre syntaxique permettant de ne pas utiliser de parenthèses.
Le site $
permet d'éviter les parenthèses. Tout ce qui apparaît après lui aura la priorité sur tout ce qui le précède.
Par exemple, disons que vous avez une ligne qui se lit comme suit :
putStrLn (show (1 + 1))
Si vous voulez vous débarrasser de ces parenthèses, n'importe laquelle des lignes suivantes fera la même chose :
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
L'objectif principal de la .
ne sert pas à éviter les parenthèses, mais à enchaîner les fonctions. Il vous permet de lier la sortie de ce qui apparaît à droite à l'entrée de ce qui apparaît à gauche. Cela permet généralement de réduire le nombre de parenthèses, mais fonctionne différemment.
Reprenons le même exemple :
putStrLn (show (1 + 1))
(1 + 1)
n'a pas d'entrée, et ne peut donc pas être utilisé avec la fonction .
opérateur.show
peut prendre un Int
et retourner un String
.putStrLn
peut prendre un String
et retourner un IO ()
.Vous pouvez enchaîner show
a putStrLn
comme ça :
(putStrLn . show) (1 + 1)
Si cela fait trop de parenthèses à votre goût, vous pouvez vous en débarrasser avec la fonction $
opérateur :
putStrLn . show $ 1 + 1
En fait, puisque + est aussi une fonction, ne pourriez-vous pas le faire préfixer puis le composer également, comme ` putStrLn . show . (+) 1 1 ` Non pas que ce soit plus clair, mais je veux dire... vous pourriez, non ?
@CodexArcanum Dans cet exemple, quelque chose comme putStrLn . show . (+1) $ 1
seraient équivalentes. Vous avez raison de dire que la plupart (tous ?) des opérateurs infixes sont des fonctions.
Après avoir vu putStrLn $ show $ 1 + 1
Je pense honnêtement, $
doit être remplacé par un caractère de poids léger. Je ne peux pas penser à un personnage pour le moment, parce qu'ils sont tous utilisés, mais un personnage va émerger.
Ils ont différents types et différentes définitions :
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
($)
est destiné à remplacer l'application normale de la fonction mais à une précédence différente pour aider à éviter les parenthèses. (.)
sert à composer deux fonctions ensemble pour en faire une nouvelle.
Dans certains cas, ils sont interchangeables, mais ce n'est pas vrai en général. L'exemple typique où ils le sont est le suivant :
f $ g $ h $ x
\==>
f . g . h $ x
En d'autres termes, dans une chaîne de $
tous sauf le dernier peuvent être remplacés par .
Notez également que ($)
es la fonction d'identité spécialisée dans les types de fonctions . La fonction d'identité ressemble à ceci :
id :: a -> a
id x = x
Alors que ($)
ressemble à ça :
($) :: (a -> b) -> (a -> b)
($) = id
Notez que j'ai intentionnellement ajouté des parenthèses supplémentaires dans la signature du type.
Utilisations de ($)
peut généralement être éliminé en ajoutant des parenthèses (sauf si l'opérateur est utilisé dans une section). Par exemple : f $ g x
devient f (g x)
.
Utilisations de (.)
sont souvent un peu plus difficiles à remplacer ; ils nécessitent généralement un lambda ou l'introduction d'un paramètre de fonction explicite. Par exemple :
f = g . h
devient
f x = (g . h) x
devient
f x = g (h x)
J'espère que cela vous aidera !
"Notez que j'ai intentionnellement ajouté des parenthèses supplémentaires dans la signature du type." Je suis confus... pourquoi as-tu fait ça ?
@MateenUlhaq Le type de ($) est (a -> b) -> a -> b, ce qui est identique à (a -> b) -> (a -> b), mais les parenthèses supplémentaires ajoutent ici un peu de clarté.
($)
permet d'enchaîner des fonctions sans ajouter de parenthèses pour contrôler l'ordre d'évaluation :
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
L'opérateur de composition (.)
crée une nouvelle fonction sans spécifier les arguments :
Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'
Prelude> let second = head . tail
Prelude> second "asdf"
's'
L'exemple ci-dessus est sans doute illustratif, mais ne montre pas vraiment l'intérêt d'utiliser la composition. Voici une autre analogie :
Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
Si nous n'utilisons le tiers qu'une seule fois, nous pouvons éviter de le nommer en utilisant un lambda :
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
Enfin, la composition nous permet d'éviter le lambda :
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.