11
votes

Pourquoi préférer le currying aux arguments de tuple dans OCAML?

"Introduction à CAML" dit < / p>

Remarque, dans CAML, il est préférable d'utiliser les définitions de fonctions au curry pour des fonctions d'argument multiples, pas des tuples.

Lors de la comparaison 'A ->' B -> 'C appelant des conventions à ' A * 'B ->' C . .

Lorsque vous travaillez avec SML / NJ, je me suis habitué à utiliser des types de tuple pour l'entrée et la sortie: ('a *' b) -> ('c *' d) donc en utilisant des utilisateurs pour exprimer Plusieurs entrées semblent symétriques avec la façon dont j'exprime plusieurs sorties.

Pourquoi le curry est-il recommandé pour les déclarations de fonction OCAML sur les arguments de tuples? Est-ce juste la plus grande flexibilité avec l'évaluation du curry / une évaluation partielle, ou existe-t-il un autre avantage qui provient des détails de la mise en œuvre du compilateur OCAML?


9 commentaires

Le choix du currying la plupart des fonctions dans les versions CAML-Light et ultérieures est expliqué dans le rapport "L'expérience de zinc: une mise en œuvre économique du langage ML". Une chose que je me souviens, c'est qu'avec le schéma d'évaluation approprié (décrit dans le rapport), une fonction au curry ne nécessite aucune allocation pour être appelée. caml.inria.fr/pub/papers/xleroy-zinc.ps. gz


@Pascalcuoq, alors qu'un tuple doit être alloué, déballé, puis flanc?


Oui, si la fonction est également destinée à être appelée avec des tuples préexistants ( ft ), il n'y a aucun moyen du fait qu'un tuple temporaire de courte dure (x, y) < / Code> doit être attribué à appliquer F à quand ce que l'on a est uniquement x et y .


@Pascalcuoq, OK. Donc, je ne devrais donc pas supposer que ocamlc analyse-analyse pour empiler - allouer des tuples temporaires et similaires.


C'est une autre question, mais de répondre, non, vous ne devriez pas. La dernière fois que j'ai vérifié, il n'a même pas optimisé l'allocation de A, B dans correspondant a, B avec x, y -> . Si vous souhaitez vérifier vous-même, j'ai trouvé que lire l'assemblage X86 généré par ocamlopt -s était pratique pour moi parce que je n'avais pas eu à apprendre une nouvelle représentation.


@Pascalcuoq, bon à savoir. Merci pour la pointe sur -s .


Juste pour être claire, les fonctions tuples n'exigent généralement pas d'allocation ni dans les compilateurs qui l'optimisent, comme c'est le cas dans la plupart des implémentations de SML. N-Ary Argument / Résultats Des tuples sont généralement compilés à N arguments / résultats dans le cadre d'optimisations d'aplatissement standard. Ce n'est que lorsque un tuple argument / résultat est administré ou utilisé de manière de première classe, une boîte de boxe / un boxing supplémentaire a lieu (c'est-à-dire exactement dans ces cas que vous ne pouvez pas exprimer sous forme de curry).


Pascal, votre premier commentaire est intéressant, provenant et une excellente réponse Andreas. Pourquoi n'avez-vous pas posté cela comme une réponse? Je suis tout à fait sûr que correspond à A, B avec x, y -> ... est optimisé; En fait, nous avons même récemment discuté une extension de cette optimisation, sur laquelle Les commentaires seraient les bienvenus.


@gasche en effet, A, B avec est optimisé. J'ai peut-être pensé à que f a b = let x, y = a, b dans un + b + x + y ;; qui n'est pas optimisé en 3.11.2. Je garderai à l'esprit l'extension que vous suggérez mais, pour la même raison que je suis en cours d'exécution 3.11.2, je ne peux pas l'essayer facilement dans mon environnement actuel.


3 Réponses :


4
votes

Oui, c'est principalement la commodité notionnelle et la flexibilité nécessaire pour faire une application partielle. Les fonctions curry sont idiomatiques dans OCAML et le compilateur risque de les optimiser quelque peu mieux que les fonctions tuples (alors que les compilateurs SML optimisent généralement pour les tuples).

Les avantages de la dosage sont l'argument / la symétrie de résultat que vous mentionnez (ce qui est particulièrement utile lors de la composition des fonctions) et peut-être la familiarité de notation (au moins pour les personnes provenant du monde non fonctionnel).


0 commentaires

2
votes

Quelques commentaires sur l'optimisation dans OCAML.

Dans OCAML, j'ai remarqué que les tuples sont toujours alloués lors de leur transmission d'arguments. Même si l'allocation du tas principal est rapide dans OCAML, il est bien sûr de ne rien faire. Ainsi, chaque fois que vous passez un tuple comme argument, il y a du temps passé à la répartition et à la remplissage du tuple.

Je m'attendais à ce que le compilateur OCAML optimise les cas où il n'est pas nécessaire de construire le tuple. Par exemple, lorsque vous en ligne la fonction appelée, vous ne pouvez utiliser que les composants de tuple et non le tuple lui-même. Ainsi, le tuple pourrait juste être ignoré. Malheureusement, dans ce cas, OCAML n'enlève pas le tuple inutile et effectue toujours l'allocation. Pour cette raison, ce n'est peut-être pas une bonne idée d'utiliser des tuples dans des sections critiques du code.


1 commentaires

Je pense que ce n'est plus vrai. "" Ocaml est plus intelligent que je ne le pensais " dit" I «D empêché l'inlinisation, mais il n'y avait toujours pas d'allocation! Pourquoi? Eh bien, il s'avère que OCAML peut optimiser une fonction de prise de tuple pour obtenir les éléments du tuple transmis via des registres, ce qui est exactement ce qui s'est passé. Et encore une fois. Le compilateur s'est rendu compte qu'aucune allocation n'était requise. "



7
votes

Je pense que beaucoup d'entre elles est la convention - Les fonctions de la bibliothèque standard dans OCAML sont currencées, alors que dans la norme ML, ils ne sont généralement pas sauf pour certaines fonctions d'ordre supérieur. Cependant, il existe une différence cuite dans la langue: les opérateurs (par exemple (*) ) sont curreux dans OCAML (E.G. int -> int -> int ); Alors qu'ils sont déconcertés dans Standard ML (E.G. op * peuvent être (int * int) -> int ). Pour cette raison, des fonctions intégrées d'ordre supérieur (par exemple) (par exemple) prennent également une fonction au fur et à mesure de l'OCAML et de la NECANDRIED dans la norme ML; Cela signifie que votre fonction fonctionne avec cela, vous devez suivre la convention respective et elle découle de là.


2 commentaires

Bon point. Il y a une autre instance où le choix est cuit dans: constructeurs de types de données. Dans le SML, ils sont tuplés, à Haskell, ils sont curreux. Un peu, dans Ocaml, ils sont tuplus, ce qui est un peu une inadéquation avec le reste de la langue.


Le sens plus profond des constructeurs de types de données étant tupled est qu'un tuple est un constructeur de données spécial anonyme.