2014-09-10 3 views
1
classe de types

implicite La dernière ligne de déclaration produit ci-dessous une erreur: "incompatibilité de type Trouvée: TestExpandableWithLibrary.this.library.type (avec org.typeclass.Library de type sous-jacent) requis: V"Scala et de puzzle

Cette est où j'essaye de faire une conversion implicite. La ligne précédente où j'utilise simplement la fonction typeclass fonctionne bien.

Des idées pour y remédier?

package org.typeclass 

/////////////////////////////////////////////////////////////////////////////// 
// the domain objects 

case class Book(bookName: String) 

case class Library(libraryName: String, books: Set[Book]) 

object Library { 
    def apply(libraryName: String, bookNames: String*): Library = 
    Library(libraryName, bookNames.map(Book(_)).toSet) 
} 

case class TreeClass(nodeName: String, children: Seq[TreeClass]) 

/////////////////////////////////////////////////////////////////////////////// 
// the typeclass definition 

trait Expandable[T, V, R] { 
    def expandWith(template: T, values: V): R 
} 

object Expandable { 

    def expandWithF[T, V, R](template: T, values: V)(implicit ev: Expandable[T, V, R]): R = 
    ev.expandWith(template, values) 

    implicit class ExpandableItem[T, V, R](val template: T) extends AnyVal { 
    def expandWithM(values: V)(implicit ev: Expandable[T, V, R]): R = 
     ev.expandWith(template, values) 
    } 
} 

/////////////////////////////////////////////////////////////////////////////// 
// a typeclass implementation 

object ExpandableImpls { 

    implicit object ExpandableTreeClass extends Expandable[TreeClass, Library, TreeClass] { 
    def expandWith(template: TreeClass, library: Library): TreeClass = { 
     val parentName = s"${template.nodeName}.${library.libraryName}" 
     val children = library.books.map(book => TreeClass(s"${parentName}.${book.bookName}", Seq.empty)).toSeq 
     TreeClass(parentName, children) 
    } 
    } 

} 

//@RunWith(classOf[JUnitRunner]) 
class TestExpandableWithLibrary /*extends FlatSpec*/ { 

    import Expandable._ 
    import ExpandableImpls._ 

    val library = Library("test", "black", "white") 
    val root = TreeClass("parent", Seq.empty) 

    val useF = expandWithF(root, library) // this works 

    val useM = root.expandWithM(library) // this doesn't work! 

} 

Répondre

4

Le problème est juste que vous avez besoin de mettre les deux paramètres de type deuxième de la classe implicite sur la méthode que l'extension est qu'ils vont être inférés être Nothing, car ils ne sont pas visés au les arguments du constructeur.

implicit class ExpandableItem[T](val template: T) extends AnyVal { 
    def expandWithM[V, R](values: V)(implicit ev: Expandable[T, V, R]): R = 
    ev.expandWith(template, values) 
} 

Ceci devrait fonctionner correctement.

+0

Parfait, merci beaucoup! – satyagraha

+0

Pour ne pas être churlish, mais peut-être quelque part le long de la ligne idéalement le compilateur aurait pu me donner quelques indices quant à l'endroit où je vais à la dérive. Type d'inférence = Dragons Soyez ici! – satyagraha

+0

@satyagraha: Je suis d'accord que l'erreur pourrait être mieux. C'est une bonne idée d'intérioriser la règle que si vous voulez un paramètre de type déduit, il devrait apparaître dans une position non générique dans les arguments, cependant. –