2017-07-24 1 views
1

J'ai une question à propos de la résolution implicits. Dites, j'ai la classe de type:Diverging expansion implicite

trait Foo[In <: Base, Out <: Base] { 
    def factor : Double 
} 

alors

sealed trait Base 
object Base { 
    implicit def symmetricFoo[In <: Base, Out <: Base](implicit foo : Foo[In, Out]) : Foo[Out, In] = 
    new Foo[Out, In] { 
     def factor : Double = 1.0/foo.factor 
    } 

    implicit def transitiveFoo[In <: Base, Mid <: Base, Out <: Base](implicit foo1 : Foo[In, Mid], foo2 : Foo[Mid, Out]) : Foo[In, Out] = 
    new Foo[In, Out] { 
     def factor : Double = foo1.factor * foo2.factor 
    } 
} 

case object A extends Base 

case object B extends Base { 
    implicit def bFoo : Foo[B.type, A.type] = 
    new Foo[B.type, A.type] { 
     def factor : Double = 2.0 
    } 
} 

case object C extends Base { 
    implicit def cFoo : Foo[C.type, A.type] = 
    new Foo[C.type, A.type] { 
     def factor : Double = 3.0 
    } 
} 

case object D extends Base { 
    implicit def dFoo : Foo[D.type, C.type] = 
    new Foo[D.type, C.type] { 
     def factor : Double = 5.0 
    } 
} 

Je voudrais être en mesure d'obtenir des instances de Foo [X, Y] si j'ai intermédiaire Foo le long du chemin de X à Y. Parfois ça marche, e. g.

println(implicitly[Foo[D.type, A.type]].factor) // 15.0 (D->C, C->A = 5 * 3) 
println(implicitly[Foo[D.type, B.type]].factor) // 7.5 (D->C, C->A, A->B = 5 * 3 * 1/2) 

mais si je change l'ordre des lignes, il n'a pas:

println(implicitly[Foo[D.type, B.type]].factor) // 7.5 
println(implicitly[Foo[D.type, A.type]].factor) // does not compile 

Il y a beaucoup de messages d'erreur plus ou moins identiques comme

test.this.Base.transitiveFoo is not a valid implicit value for test.Foo[test.C.type,test.A.type] because: hasMatchingSymbol reported error: diverging implicit expansion for type test.Foo[test.C.type,Mid] starting with method transitiveFoo in object Base

diverging implicit expansion for type test.Foo[test.A.type,Mid] starting with method transitiveFoo in object Base

not enough arguments for method implicitly: (implicit e: test.Foo[test.D.type,test.A.type])test.Foo[test.D.type,test.A.type]. Unspecified value parameter e.

Le journal complet est un peu plus longtemps. Quelle est la manière correcte, étant donné Foo [X, Y], d'obtenir Foo [Y, X], et donné Foo [X, Y] et Foo [Y, Z], pour obtenir Foo [X, Z] ] pour toutes les combinaisons de Foo définies? Quelque chose comme "L'aide paresseuse ici? Merci d'avance!

+0

Cher @Max, il a été un moment que vous avez posé votre question, mais si vous avez un moment pouvez-vous vérifier si ma [réponse] (https://stackoverflow.com/a/45924188/5249621) est ok? Je vous remercie. –

+1

@DmytroMitin, merci beaucoup! Je vais vérifier votre solution a.s.a.p. et vous donner mes commentaires. – Max

+0

avez-vous eu le temps de vérifier? :) –

Répondre

1

Cette variante semble fonctionner:

import shapeless.Lazy 

    trait Foo[In <: Base, Out <: Base] { 
    def factor : Double 
    } 

    sealed trait Base 
    case object A extends Base 
    case object B extends Base 
    case object C extends Base 
    case object D extends Base 

    trait LowPriorityImplicits { 
    implicit def symmetricFoo[In <: Base, Out <: Base](implicit foo : Lazy[Foo[In, Out]]) : Foo[Out, In] = 
     new Foo[Out, In] { 
     def factor : Double = 1.0/foo.value.factor 
     } 
    } 

    object Base extends LowPriorityImplicits { 
    implicit def bFoo : Foo[B.type, A.type] = 
     new Foo[B.type, A.type] { 
     def factor : Double = 2.0 
     } 

    implicit def cFoo : Foo[C.type, A.type] = 
     new Foo[C.type, A.type] { 
     def factor : Double = 3.0 
     } 

    implicit def dFoo : Foo[D.type, C.type] = 
     new Foo[D.type, C.type] { 
     def factor : Double = 5.0 
     } 

    implicit def transitiveFoo[In <: Base, Out <: Base, Mid <: Base](implicit foo1 : Foo[In, Mid], foo2 : Foo[Mid, Out]) : Foo[In, Out] = 
     new Foo[In, Out] { 
     def factor : Double = foo1.factor * foo2.factor 
     } 
    } 

    def main(args: Array[String]): Unit = { 
    println(implicitly[Foo[D.type, B.type]].factor) 
    println(implicitly[Foo[D.type, A.type]].factor) 
    } 

    7.5 
    15.0 

    def main(args: Array[String]): Unit = { 
    println(implicitly[Foo[D.type, A.type]].factor) 
    println(implicitly[Foo[D.type, B.type]].factor) 
    } 

    15.0 
    7.5 

Juste au cas où mon build.sbt:

scalaOrganization := "org.typelevel" 
scalaVersion := "2.12.3-bin-typelevel-4" 
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.2"