Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to extend a trait from a record type's companion object ? #61

Open
barambani opened this issue Sep 5, 2018 · 2 comments
Open

Comments

@barambani
Copy link

At the moment when generating a record (I use a SpecificRecord but I guess is the same for the others) also the companion object of the case class is (correctly) generated. So for something like

protocol TestProtocol {

  record Test {
    long id;
    string text;
  }
}

the generated code is

import scala.annotation.switch

case class Test(var id: Long, var text: String) extends org.apache.avro.specific.SpecificRecordBase {
  def this() = this(0L, "")
  def get(field$: Int): AnyRef = {
    (field$: @switch) match {
      case 0 => {
        id
      }.asInstanceOf[AnyRef]
      case 1 => {
        text
      }.asInstanceOf[AnyRef]
      case _ => new org.apache.avro.AvroRuntimeException("Bad index")
    }
  }
  def put(field$: Int, value: Any): Unit = {
    (field$: @switch) match {
      case 0 => this.id = {
        value
      }.asInstanceOf[Long]
      case 1 => this.text = {
        value.toString
      }.asInstanceOf[String]
      case _ => new org.apache.avro.AvroRuntimeException("Bad index")
    }
    ()
  }
  def getSchema: org.apache.avro.Schema = Test.SCHEMA$
}

object Test {
  val SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Test\",\"namespace\":\"com.hbc.product.streams.topology.source\",\"fields\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"text\",\"type\":\"string\"}]}")
}

Is there any way I could specify a trait that would be extended by the companion object ? Something like

object Test extends TestInstances {
  final val SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Test\",\"namespace\":\"com.hbc.product.streams.topology.source\",\"fields\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"text\",\"type\":\"string\"}]}")
}

The use case I have is that this way it would be possible to put type class instances in the implicit scope for the generated types avoiding orphans. Thanks.

@julianpeeters
Copy link
Owner

Nothing like exactly like that currently.

However there's a way to customize how case classes are generated. It uses these models. Maybe there's a way to adapt that to get what you want? Maybe something like an optional field with a default of None, or maybe with varargs?

Did you have something in mind, maybe like SpecificRecord.defaultTypes.copy(record = ScalaCaseClass("TestInstances")? Or maybe use a map Map to be more precise, SpecificRecord.defaultTypes.copy(record = ScalaCaseClass(Map("Test" -> "TestInstances"))?

@barambani
Copy link
Author

The issue I have here is that, when I define a type in idl I would need also to place the typeclass instances for that type in the companion object, so what would help is a way to tell the generator how to place those instances in there. As an example consider I wanted to put the instance for Show[Test] in the implicit scope. I could place it in TestInstances as here

trait TestInstances {
  implicit final val testShow: Show[Test] = ???
}

Or even create a hierarchy that would give me different priorities like

trait TestInstances extends LowPriorityTestInstances {
  implicit final val testShow: Show[Test] = ???
}
sealed trait LowPriorityTestInstances {
  /** other low priority instances here **/
}

Anything that works on the way the case class is generated or on the type I'm afraid doesn't work.
May be an option could be to exploit the annotations that are already used for language specific features (like namespaces, ordering etc.). Something like

protocol TestProtocol {

  @companion-is-a("my.fully.qualified.namespace.TestInstances") or
  @companion-extends("my.fully.qualified.namespace.TestInstances")
  record Test {
    long id;
    string text;
  }
}

with the care of making SCHEMA$ final in this case and considering that TestInstances will be part of the manually generated code (so it will be there when the compilation is attempted or it will generate a compilation error).
Anyway I will try again with the existing configuration options as you suggest to see if I find a way.
Thanks a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants