1
votes

Une fonction avec un argument fonctionnel est-elle curry?

Depuis https://stackoverflow.com/a/57020455/156458

Dans Haskell, il arrive que tout soit curry ; toutes les fonctions ne prennent qu'un seul argument (même les fonctions non curry dans Haskell prennent un tuple, qui est, à proprement parler, un seul argument - vous voudrez peut-être jouer avec les fonctions curry et uncurry pour voir comment cela fonctionne ).

Je ne suis pas sûr que ce soit vrai, mais je suppose que oui.

Si une fonction prend une autre fonction comme argument, est-elle curry ou non curry dans un sens similaire à une fonction prenant un argument tuple ou liste non curry? (Un type de tuple paire est type1 x type2 et peut être type1 ^ 2 , tandis qu'un type de fonction est type2 ^ {type1} , donc je trouvez-les similaires)

Si je ne me trompe pas, comment puis-je convertir une telle fonction en une fonction curry?


4 commentaires

Une fonction n'est qu'une valeur que vous pouvez prendre comme paramètre. Étant donné qu'une fonction prenant une autre fonction comme argument, le curry n'a pas d'importance de toute façon si la fonction ne prend pas plusieurs valeurs?!


" Je les trouve similaires " - mais ils ne le sont pas. Vraiment pas.


Le curry concerne en fait l'isomorphisme entre les types a -> b -> c et (a, b) -> c . La forme curry est plus pratique, car elle permet une utilisation plus simple de l'application partielle, et elle «évolue» mieux. L'isomorphisme curry / non curry en lui-même ne convertit pas quelque chose de type a -> b -> c -> d en quelque chose de type (a, b, c) -> d ; il ne peut vous donner que quelque chose de type ((a, b), c) -> d . Vous avez besoin d'un deuxième isomorphisme pour aplatir ((a, b), c) en (a, b, c) .


"... alors qu'un type de fonction est type2 ^ {type1} " non, la distinction est entre type3 ^ (type1 x type2) et (type3 ^ type2) ^ type1 . "comment convertir en une fonction curry" en utilisant curry . si f est "non curry", curry f est "curry".


3 Réponses :


5
votes

Si une fonction prend une autre fonction comme argument, est-elle curry ou non?

Cela ne prend qu'une fonction, donc un seul paramètre, et donc il est curry. Le fait que ce paramètre soit une fonction n'a pas d'importance.

Une telle fonction est par exemple map . map est de type:

map :: (a -> b) -> ([a] -> [b])

Elle prend donc un seul paramètre, une fonction (de type a -> b ) , puis renvoie une fonction [a] -> [b] qui mappera une liste de a à une liste de b par appliquant cette fonction.

Donc, map show :: Show a => [a] -> [String] est le résultat d'une telle application de fonction, et c'est encore une fonction.


2 commentaires

Merci. Est-ce «non curry» dans un sens similaire à une fonction prenant un argument tuple ou liste étant «non curry»? Si "non curry", comment puis-je convertir une telle fonction en fonction curry?


@Tim No. Une fonction est une valeur unique. Un tuple ou des listes se compose de plusieurs valeurs, c'est la seule raison pour laquelle nous appellerions une fonction Haskell «non curry».



3
votes

Oui, la citation est correcte. Tout le discours sur les fonctions "curry" et "non curry" est un jargon imprécis.

Haskell est un langage curry. Les fonctions dans Haskell ont toujours des types exponentiels. Si un argument est un tuple, peu importe, il ne s'agit que d'une seule valeur (qui se trouve être un tuple).

Les concepts sont approchés lorsque nous traitons un (a, b ) -> c Haskell fonction comme c a * b . Mais c'est juste une gymnastique mentale que nous faisons.

Ce qui est réellement curieux ou non, ce sont les langages de programmation . Par exemple, en Lisp,

(lambda (a b) c)

a en fait le type c a * b et pour le transformer en fonction (c b ) a , nous devons lui faire subir quelques transformations.

Il n'y a en fait pas de lambdas (\ ab -> c) dans Haskell, seulement (\ a -> (\ b -> c)) Lambdas imbriquées ( * ) . Quand on écrit (\ a b -> c) en Haskell, c'est juste un raccourci syntaxique pour (\ a -> (\ b -> c)) . Il est impossible d'avoir une fonction lambda (\ ab -> c) réelle dans Haskell, bien qu'elle soit approximée en ayant (\ (a, b) -> c) lambda

Où vous voyez vraiment le sens de tout cela, si vous implémentez votre propre langage avec des fonctions lambda.

Face à un appel de fonction ((lambda (ab) c) xyz) , le vrai problème est de comment coupler les paramètres et les valeurs fournies.

Haskell le convertit en ((let a = x in (let b = y in c)) z) , mais Lisp associe en fait la liste de paramètres (ab ) avec la liste de valeurs (xyz) et signale l'incohérence de longueur.

Mais, étant le langage non curry qu'il est, Lisp est capable d'avoir les divers rebondissements et ajustements ici, comme des arguments optionnels, des arguments par défaut, des arguments nommés, etc. les paramètres et les valeurs de différentes manières - contrairement à Haskell, qui toujours associe un paramètre avec une valeur fournie à la fois.


( * ) et avec une autre distinction cruciale: le code a et b > dans le (\ a -> (\ b -> c)) de Haskell ne sont pas des variables, mais des modèles . Les valeurs ne leur sont pas simplement assignées, comme en Lisp - elles sont mises en correspondance avec elles.


0 commentaires

2
votes

La vérité que je peux voir au moins, c'est que presque chaque valeur de haskell peut être vue comme une fonction avec, et chaque fonction prend juste un paramètre à la fois. Voyons un exemple (avec Int comme exemple, pour être plus clair):

(h f 2 3) 4 == 7

f peut être vu comme une fonction qui prend un Int et renvoie une fonction

 :t (h f 2 3)
(h f 2 3) :: Int -> Int

(f 2 3) peut être vu comme une fonction qui prend un Int et renvoie un Int p >

enfin

:t (h f 2)
(h f 2) :: Int -> Int -> Int

Un exemple avec des fonctions d'ordre supérieur:

(h f) :: Int -> Int -> (Int -> Int)

Un peu plus complexe mais tout de même idée:

:t (h f)
(h f) :: Int -> Int -> Int -> Int

(hf) est une fonction, attendant un Int , et renvoyant (Int -> Int - > Int -> Int)

mais ... attendez, ne s'attendait-il pas à renvoyer une fonction? il devrait en être ainsi

h :: (Int -> Int -> Int) -> Int -> Int -> Int
h fn x y = fn x y

eh bien, remarquez. continuons

:t (f 2 3 4)
(f 2 3 4) :: Int

(hf 2) est une fonction attendant un Int et renvoyant une fonction (Int -> Int )

et enfin

Int -> (Int -> Int)


:t (f 2)
(f 2) :: Int -> Int -> Int
:t (f 2 3)
(f 2 3) :: Int -> Int

(hf 2 3) est en effet une fonction, attendant un Int code>, renvoyant un Int

f :: Int -> Int -> Int -> Int
f x y z = x + y + z

Je pense que la conclusion ici est, chaque fonction est curry dans Haskell .


2 commentaires

(1) Bien que ce soit en grande partie correct, je crois qu'une ligne devrait être tracée dans le cas d'absence d'arguments. En fin de compte, une fonction Haskell est quelque chose avec (->) dans son type, et donc 3 :: Int n'est pas une fonction. (Voir aussi conal.net/blog/posts/everything-is -une-fonction-dans-haskell ). Je suggère simplement de laisser tomber ce cas de cette réponse. (2) "Prend un Num a n'est pas entièrement précis, car Num a n'est pas un type." Prend un a (avec a étant une instance de Num ) "est mieux. (3) Sur une note connexe, rendre f monomorphe (par exemple, f :: Int -> Int -> Int -> Int ) rendrait votre exemple plus simple.


@duplode merci beaucoup, je pense que c'est plus clair maintenant, j'ai suivi vos conseils