2010-08-11 19 views
13

Si j'ai une fonction:Pourquoi taitement et uncurrying pas implicite dans scala

f : A => B => C 

Je peux définir une conversion implicite telle que cela peut être utilisé lorsqu'une fonction (A, B) => C est attendue. Cela va aussi dans l'autre sens.

Pourquoi ces conversions ne sont-elles pas implicite (ou disponibles implicitement)? Je suppose que de mauvaises choses pourraient arriver pour une certaine valeur de mauvaises choses. Quelle valeur est-ce?

Répondre

12

Je ne pense pas que quelque chose de mauvais va arriver. La conversion est complètement non ambiguë. Dans le pire des cas, Scala ne pourra pas comprendre que la conversion implicite s'applique.

implicit def curryImplicitly[A,B,C](f: (A, B) => C) = 
    (a: A) => (b: B) => f(a, b) 
implicit def uncurryImplicitly[A,B,C](f: A => B => C) = 
    (a: A, b: B) => f(a)(b) 

Ensuite, ceux-ci seraient également utiles.

implicit def flipImplicitly[A,B,C](f: (A, B) => C) = 
    (b: B, a: A) => f(a, b) 
implicit def flipImplicitlyCurried[A,B,C](f: A => B => C) = 
    (b: B) => (a: A) => f(a)(b) 

Mais ce ne sont pas transitif, vous devez donc celles-ci:

implicit def flipAndCurry[A,B,C](f: (A, B) => C) = 
    (b: B) => (a: A) => f(a, b) 
implicit def flipAndUncurry[A,B,C](f: A => B => C) = 
    (b: B, a: A) => f(a)(b) 

Mais maintenant, la conversion est ambiguë. Donc, ce ne sont pas toutes des roses.

Apprenons comment cela fonctionne dans la pratique. Vous pourriez avoir besoin d'équivalents pour Function3, Function4, etc.

+0

Je n'ai pas joué avec cela dans 2.8, mais j'ai essayé cela dans les jours sombres de 2.7.X et il a tendance à conduire à des plantages du compilateur, dans le type inferencer iirc. Les choses se sont un peu améliorées sur ce front, alors peut-être que tout va bien maintenant ... –

+0

Ouais, il est toujours trop facile de planter le compilateur si vous essayez de le faire inférer d'un type plus élevé, mais c'est une énorme amélioration par rapport à 2.7. – Apocalisp

+0

J'ai essayé ceux-ci en 2,8 pour les cas simples, et tout s'est bien passé. – thSoft

8

Vous ne voulez pas qu'ils soient implicitement disponibles par défaut (toujours allumé) parce que le système de type a du mal à vous aider quand vous avez surchargé avec des arguments d'un tas de types similaires:

A => B => C 
D => C  // D is allowed to be a tuple (A,B)... 

(A,B) => C // If I have this, to whom should I convert? 

Une partie de l'avantage de la frappe forte vous avertit lorsque vous avez fait quelque chose d'idiot. Essayer trop fort de faire fonctionner les choses réduit les avantages. Ici, si les conversions ont été effectuées automatiquement, vous pourriez ne pas appeler la méthode que vous vouliez appeler. Les avoir disponibles implicitement sur demande, c'est bien, mais ce n'est pas si difficile à faire soi-même si vous en avez besoin. C'est quelque chose que j'utiliserais très rarement; Je ne le mettrais pas dans mon top dix ou probablement même les cent premières choses que je voudrais dans la bibliothèque (en partie parce que je pourrais préférer la conversion automatique à un tuple au lieu de l'automatique currying/uncurrying).

+4

Notez que vous ne serez pas en mesure de surcharger une fonction avec les arguments 'A => B => C' et' D => C', car ils ont le même effacement. Donc, le problème ne viendra pas de cette façon dans la pratique. – Apocalisp

+0

Ah, c'est vrai. Il peut toujours être utile d'avoir des signatures de type comme une double vérification si vous utilisez des noms de méthodes différents mais que le nom de la méthode est incorrect. –