2017-01-20 1 views
4

Je le code suivant qui était censé prendre une fonction A => Boolean (générique du type d'entrée) et le convertir en un trait Y[A] générique par des conversions implicites enchaînées:Chaînage conversions implicites de fonction à une classe générique

val f: Int => Boolean = ??? 

trait X[A] { 
    def m1: Unit 
} 
implicit def toX[A](f: A => Boolean): X[A] = ??? 
f.m1 // Works 

trait Y[A] { 
    def m2: Unit 
} 
implicit def toY[T, A](x: T)(implicit toX: T => X[A]): Y[A] = ??? 
f.m2 // Won't compile 

Malheureusement, la dernière ligne ne sera pas compilée.

Faire une une des changements suivants est suffisant pour permettre la compilation de code:

  • En ce X non générique
  • Turning Y non générique
  • Remplacement du type de source (qui est une fonction générique sur le type d'entrée) avec une fonction générique sur le sortie type (Int => A)
  • Re placer le type de source avec d'autres types génériques tels que Option[A], Seq[A] ou Array[A]
Sur la base de cette

, ma conclusion est que la chaîne de conversions implicites ne fonctionnera pas parce que le type de source (fonction générique du type d'entrée) est générique et contravariant et les types intermédiaires et cibles (X[A] et Y[A]) sont génériques.

Des idées pour résoudre ce problème?

MISE À JOUR: Le code final est en retrait pour gérer non seulement une fonction en tant que type de source, par d'autres types aussi bien (Option[A], Seq[A], A, etc.). Pour ce faire, l'idée est d'avoir des versions de la fonction toX qui convertissent chacun de ces types en X[A]. Alors seulement une version de toY est désirée.

Répondre

6

Je pense avoir une solution pour vous problème, consultez le code suivant:

val f: Int => Boolean = _ => true 

trait X[A] { 
    def m1: Unit 
} 
implicit def funcToX[A](f: A => Boolean): X[A] = new X[A] { 
    override def m1: Unit = println("Hello x") 
} 
f.m1 // Works 

trait Y[A] { 
    def m2: Unit 
} 
implicit def toY[T[_,_], A](x: T[A, Boolean])(implicit toX: T[A, Boolean] => X[A]): Y[A] = new Y[A] { 
    override def m2: Unit = { 
    x.m1 
    println("Hello y") 
    } 
} 
f.m2 // now works 

J'utilise ici la syntaxe de type plus kinded. C'est une fonctionnalité avancée de la langue scala et je ne suis pas expirienced assez pour l'expliquer correctement. La conversion vers Y devrait fonctionner pour n'importe quel type qui prend exactement deux types de paramètres (et a défini la conversion "toX").

La question est de savoir si vous avez vraiment besoin de conversion toY pour être générique sur le paramètre T?Peut-être qu'il suffit d'accepter les fonctions comme arguments:

implicit def toY[A](x: A => Boolean)(implicit toX: (A => Boolean) => X[A]) = ??? 

Vous pouvez en savoir plus sur les types kinded plus élevés, par exemple ce poste:

Scala: Types of a higher kind

Mise à jour

Suite l'auteur de la question exige que je suis venu avec la solution suivante:

type SingleGenericFun[T] = T => _ 

val f: SingleGenericFun[Int] = _ > 42 
val g: Int = 42 

trait X[A] { 
    def m1: Unit 
} 
implicit def toX(f: SingleGenericFun[Int]): X[Int] = ??? 
implicit def toX(x: Int): X[Int] = new X[Int] { 
    override def m1: Unit = println(x) 
} 
f.m1 // Works 

trait Y[A] { 
    def m2: Unit 
} 
implicit def toY2[T[_, _], A](x: T[A, _])(implicit toX: T[A, Boolean] => X[A]): Y[A] = new Y[A] { 
    override def m2: Unit = { 
    x.m1 
    println("Hello y!") 
    } 
} 
implicit def toY0[A](x: A)(implicit toX: A => X[A]): Y[A] = new Y[A] { 
    override def m2: Unit = { 
    x.m1 
    println("Hello y!") 
    } 
} 
implicit def toY1[T[_], A](x: T[A])(implicit toX: T[A] => X[A]): Y[A] = new Y[A] { 
    override def m2: Unit = { 
    x.m1 
    println("Hello y") 
    } 
} 
g.m2 
f.m2 // Compile 

Ce n'est toujours pas la meilleure solution car il faut fournir 3 (ou même plus) méthodes qui techniquement font la même chose, je ne sais pas comment le générer, ni si c'est possible dans le premier endroit.

+0

Ma véritable implémentation utilise déjà des types plus élevés, mais d'une manière très différente. Le code que j'ai posté est juste une version dépouillée essayant d'isoler le problème. Le code final traitera non seulement les fonctions, mais aussi d'autres types de sources ('Option [A]', 'Seq [A]', 'A' lui-même, etc.). Chacun de ces types aura une version de '* toX', mais étant donné une conversion d'un type en' X [A] ', une seule version de' toY' est souhaitée. Donc, votre solution ne fonctionnera pas. :-( – erdavila

+0

Je vais essayer de mettre ces exigences sur ma question. – erdavila