Scala Can't Find Implicit and I Don't Know Why - Can Someone Point Me in the Right Direction?

1.1k Views Asked by At

I'm trying to write a factory to create case classes. I'm creating a command line tool that accepts String representations for the parameters of the case class. Anyway the idea being to limit changes for additions to creating an implicit for the new type, but I can't seem to get past the starting line. Below is some code that attempts to create a case object (so no parameters - the most trivial case), but scalac complains that it "cannot find the implicit value for parameter factory". Gotta be something stoopid, but I've tried everything I can think of. If anyone can point me in the right direction, it would be most appreciated. Thanks.

    object Message {

      case object Person


      sealed trait Repr[T] {
        def value: T
      }


      trait Parm0Factory[R] {
        def instance(): R
      }


      implicit val personFactory = new Parm0Factory[Person.type] {
        def instance() = Person
      }


      case class Parm0[R]() extends Repr[R] {
        override def value(): R = xform

        private def xform(implicit factory: Parm0Factory[R]): R = factory.instance()
      }

   def main(args: Array[String]): Unit = { 
      val t1 = Parm0[Person.type]() 
      println(t1.value) 
   }

    }
2

There are 2 best solutions below

6
On

You have an implicit definition for PersonFactory[Person.Type], but what your xform call is looking for and not finding is PersonFactory[R]. It is not the same type.

0
On

I did some research and was able to come up with an implementation of my idea that for now is quite satisfying (at least for me). Briefly, I wanted to maintain a String representation of a recursive tree structure that a user could modify in place, then be able to convert that representation into an actual instance. Turns out type classes are what I was looking for. Thought I would go ahead and post this for those out there that are at least as unfamiliar with using type classes as I was. If anyone knows how to remove the necessity of having to provide the type parameter to the invocation of 'build', please let me know. Also, I'd like to have a cleaner implementation of the implicit builder functions that doesn't use the '@unchecked' annotation to eliminate the annoying compiler warnings. :-)

object Messages {

  // Test classes
  case class Person(name: String, age: Int, pet: Dog)

  case class Dog(name: String)

  case class Device(deviceType: DeviceType, on: Boolean)

  sealed trait DeviceType

  case class Android() extends DeviceType

  case class iOS() extends DeviceType

  case class Windows() extends DeviceType



  // Builders...
  trait Builder[A] {
    def build: Node => A
  }

  object Builder {
    def build[A](f: Node => A) = new Builder[A] {
      val build = f
    }

    // Terminals...
    implicit val intBuilder: Builder[Int] = build(node => (node: @unchecked) match {
      case Term(_, value) => value.toInt
    })
    implicit val stringBuilder: Builder[String] = build(node => (node: @unchecked) match {
      case Term(_, value) => value
    })
    implicit val booleanBuilder: Builder[Boolean] = build(node => (node: @unchecked) match {
      case Term(_, value) => value.toBoolean
    })


    // Case classes (composites)
    implicit val dogBuilder: Builder[Dog] = build[Dog](node => (node: @unchecked) match {
      case Tree(_, children) =>
        Dog(children(0).build[String])
    })

    implicit val personBuilder: Builder[Person] = build[Person](node => (node: @unchecked) match {
      case Tree(_, children) =>
        Person(children(0).build[String], children(1).build[Int], children(2).build[Dog])
    })

    implicit val deviceTypeBuilder: Builder[DeviceType] = build[DeviceType](node => (node: @unchecked) match {
      case Term(_, value) => value match {
        case "Android" => Android()
        case "iOS" => iOS()
        case "Windows" => Windows()
      }
    })

    implicit val deviceBuilder: Builder[Device] = build[Device] (node => (node: @unchecked) match {
      case Tree(_, children) => Device(children(0).build[DeviceType], children(1).build[Boolean])
    })
  }


  // Data Structures...
  sealed trait Node {
    val name: String

    def prompt: String

    def build[A: Builder]: A = implicitly[Builder[A]].build(this)
  }

  case class Tree(name: String, children: IndexedSeq[Node]) extends Node {
    override def prompt = {
      val choices = children.zipWithIndex.map { case (node, idx) => s"${idx + 1}) ${node.name}" }.mkString("\n")
      s"$toString\n$choices\n"
    }

    override def toString = name + children.mkString("(", ", ", ")")
  }

  case class Term(name: String, value: String) extends Node {
    override def prompt = s"$name = "

    override def toString = s"$name = $value"
  }


  def main(args: Array[String]): Unit = {

    val person = Tree("Person", IndexedSeq[Node](Term("name", "Fred"), Term("age", "45"),
      Tree("pet", IndexedSeq[Node](Term("name", "Fido")))))

    val device = Tree("some device", IndexedSeq[Node](Term("type", "iOS"), Term("on", "true")))

    println(person.prompt)
    println(person.toString)

    // TODO How to remove necessity of providing type parameter?
    println(person.build[Person])

    println(device.build[Device])

  }

}