2017-10-18 29 views
1

Je voudrais créer une fonction avec la signature suivante:Conversion à tuple avec par nom paramètre

def myFunction[T](functionWithName: (String, => T)): T 

pour que je puisse l'appeler, par exemple, comme ceci: val x = myFunction("abc" -> 4 * 3). Cependant, Tuple n'accepte pas le paramètre by-name, donc la signature ci-dessus est invalide.

Inspiré par this answer, j'ai essayé la conversion implicite suivante:

implicit class ByName[T](getValue: => T) extends Proxy { 
    def apply(): T = getValue 
    def self = apply() 
} 

def myFunction[T](functionWithName: (String, ByName[T])): T = { 
    // do something 
    functionWithName._2() 
} 

L'implicite ne fonctionne pas dans ce cas, cependant (contrairement à la réponse liée).

  • Pourquoi la conversion implicite en ByName ne fonctionne pas?
  • Comment puis-je obtenir l'effet désiré en appelant myFunction("abc" -> 4 * 3)4 * 3 est passé par son nom?
+1

Pourquoi un tuple, ce qui ne va pas avec 2 paramètres? –

+1

@ insan-e Il y aura effectivement plusieurs variantes de myFunction. Il peut être appelé comme 'myFun (" a "-> 1 * 2," b "-> 3 * 4)' ou 'myFun (" a "-> 1 * 2," b "-> 3 * 4," c "-> 5 * 6)', etc. Parce qu'il peut y avoir beaucoup d'arguments, je voudrais éviter toute syntaxe inutile pour le garder lisible. – Mifeet

+1

Je vois. Vous ne pouvez pas le faire probablement parce que [tuples] (https://github.com/scala/scala/blob/v2.12.3/src/library/scala/Tuple2.scala#L19) ont des arguments enthousiastes. La seule façon que je vois est de faire le nom entier de tuple, 'functionWithName: => (String, T)' ... –

Répondre

1

J'ai deux propositions pour mettre en œuvre ce:

  • Marque tuple entier sous-nom: functionWithName: => (String, T)

  • Faites votre propre classe:

    class MyTuple[A, B](a: A, b: => B) { 
        def _1: A = a 
        def _2: B = b 
        def toTuple = a -> b // optional 
    } 
    

et une méthode personnalisée implicite quelque part, comme -> pour Tuple2 (voir ArrowAssoc en Predef):

implicit final class ArrAssoc[A](private val self: A) extends AnyVal { 
    @inline def -->[B](y: => B): MyTuple[A, B] = { 
     new MyTuple(self, y) 
    } 
    } 

Alors on pourrait dire ceci:

val t = 1 --> { println("blah"); 5 } 
    //t._2 
    //t.toTuple 

Le b para mètre ne doit pas être évalué jusqu'à ce que vous appelez t._2 Ou même faire toTuple conversion imlicit, donc quand Tuple2 est espected vous pouvez passer un MyTuple ...

4

Vous pouvez remplacer le paramètre call-by-name par un thunk.

def myFunction[T](functionWithName: (String,() => T)): T = functionWithName._2() 

myFunction(("abc",() => 4 * 3)) // 12 

ou pour le faire fonctionner avec le implicit, il vous suffit de fournir explicitement le type à myFunction:

myFunction[Int]("abc" -> 4 * 3) // 12 
+0

Oui, c'est une autre option. Existe-t-il un moyen de se passer des 2 parenthèses et de l'opérateur de flèche inutiles? Des idées pour lesquelles la conversion implicite ne fonctionne pas? – Mifeet

+0

@Mifeet Voir ma mise à jour. Le 'implicite' fonctionne si vous fournissez le type. – adrice727