2

I am trying to setup a case class hierarchy in Scala that uses companion objects. Each case class has a companion object that has a implicit method, however, I would like to make a abstract class that case classes can extend. The problem is that I have no way to make the abstract class's companion object abstract as well. Below is the code, I currently have:

abstract class Call {}

abstract class ArgumentsCall extends Call {}

trait Jsonable[T] {
        implicit def convert: CodecJson[T]
}
case class VersionCall(version:String) extends Call  //{"version": "0.1"}
object VersionCall {
        implicit def convert: CodecJson[VersionCall] = casecodec1(VersionCall.apply, VersionCall.unapply)("version")
}

case class CommandCall(command:String,arguments:ArgumentsCall) extends Call //{"command": "pcap-file", "arguments": {"output-dir": "/opt/suricata_out/1", "filename": "/opt/suricata_fifo/1"}}
object CommandCall {
        implicit def convert: CodecJson[CommandCall] = casecodec2(CommandCall.apply, CommandCall.unapply)("command","arguments")
}
case class PcapCall(outputDir:String,filename:String) extends ArgumentsCall
object PcapCall extends ArgumentsCall {
        implicit def convert: CodecJson[PcapCall] = casecodec2(PcapCall.apply, PcapCall.unapply)("output-dir","filename")
}

case class Response(returnS:String, message:Option[String])
object Response {
        implicit def convert: CodecJson[Response] = casecodec2(Response.apply, Response.unapply)("return","message")
}

The compiler is unable to determine that ArgumentsCall or it's child would have the "implicit def convert:CodecJson". Here is the actual compiler error:

[error] /blah/src/main/scala/helpers/JsonApi.scala:24: could not find implicit value for evidence parameter of type argonaut.EncodeJson[helpers.ArgumentsCall]
[error]     implicit def convert: CodecJson[CommandCall] = casecodec2(CommandCall.apply, CommandCall.unapply)("command","arguments")
[error]                                                                                                      ^
[error] one error found
[error] (compile:compile) Compilation failed

Can someone please give me a good way to reason about class/companion objects and how to do inheritance with them? I have a feeling I have a fundamental misunderstanding about how to implement this.

Thanks

6
  • 2
    Hi! As a first impression: I would be more careful regarding the conclusion "The compiler is unable to determine that ArgumentsCall or it's child would have the implicit def convert:CodecJson". I can't see how you get to this conclusion, since the actual compilation message points into another direction: it looks rather that the compiler can't create the method body, since it cant'f find an implicit value used in the second parameter list of casecodec2 Commented Jul 12, 2013 at 23:13
  • 1
    -- that means, it's irrelevant that you're about to define an implicit method, since the compiler is struck with fining an implicit value used in the body of the method. That both are "implicit" is purely coincidental. At least that is my gut feeling when reading this compiler message Commented Jul 12, 2013 at 23:13
  • as a general rule, you should try to decompose things into small and more manageable bits. While this is a gerneral "clean code rule", this is especially important in Scala, since things can get horribly complex quickly. Commented Jul 12, 2013 at 23:16
  • In your case, you're trying to package three things into a single system of definitions: (1) a structural data type (the case classes). (2) an implicit conversion. (3) the concrete selection of the codec. Commented Jul 12, 2013 at 23:17
  • 2
    Thus I would immediately separate these three. Mix in the implicit conversion using a trait. This implicit conversion just delegates to an implementation method to pick the right codec. And then this implementation method could use pattern matching on the structural data type (case classes). This way you could get each bit to work separately. Commented Jul 12, 2013 at 23:19

1 Answer 1

4

The inheritance hierarchy and companion objects are two completely distinct matters. The class and its companion object just share a namespace. Indeed, the companion object can inehrit from something completely unrelated, it can mix in traits and the like.

trait Helper {
  def multiply(n:Int) = n*2
}

object AA extends Helper {
    private def fun(i:Int) = multiply(i)
}

class AA {
   def doubleFun(jj:Int) = AA.fun(jj)
}

Note the following

  • The member function doubleFun can use the private function from the companion object
  • the method fun can use the multiply mixed in from the helper trait
  • but this doesn't mean that an instance of class AA (i.e.val a = new AA) would inherit from the Helper thread! In fact, it does not. It could inherit or mix in completely unrelated other traits
Sign up to request clarification or add additional context in comments.

5 Comments

Ichthyo, if I read you right. You are saying that first, I should move all of the implicit methods to a trait and mix them in. From there would it make sense to do away with the objects, since I could just mix in the trait to the case class? Or am i missing something. Thank you for the great responses.
possible... but I'm not sure I figured out precisely what you want to achieve. Since you're defining implicit methods, I'd guess that you're looking for some automagic creation of your case class instances right? But if so, then you would need only one implicit method, but this implicit method needs to take a parameter, and you need some way to figure out from this parameter what case class to instantiate.
when you want an automatic coversion, the implicit method doing this conversion just needs to be in scope. Thus, it might be defined within an enclosing (outer) class / Object / trait. Or you may import it into the current scope. Or you may define it in the companion object of the target type. But there is a crucial requirement: in any situation where you want an automagic conversion, the compiler must be able to find one and only one possible implitit method to apply for the given parameter type.
this blog post contains a detailed explanation... tomjefferys.blogspot.de/2011/11/…
Ichthyo, your suggestion was very helpful. I ended using a trait as you suggested, and then had the object extend that trait. Thank you for your thoughtful responses!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.