question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Case-to-Case inehritance generated

See original GitHub issue

Hey,

i came across this case of invalid scala code generated - basically if you have a definition which contains allOf with just one other definition, case class extension hierarchy is generated.

The swagger.json:

{
  "swagger" : "2.0",
  "info" : {
    "title" : "someapp",
    "description" : "someapp",
    "version" : "1"
  },
  "basePath" : "/v1",
  "schemes" : [ "http", "https" ],
  "produces" : [ "application/json" ],
  "paths": {},
  "definitions": {
    "PairedDeviceInfo" : {
      "description" : "Data about a device's pairing.",
      "allOf" : [ {
        "$ref" : "#/definitions/PairedDeviceFields"
      } ]
    },
    "PairedDeviceFields" : {
      "description" : "Fields describing the pairing of a managed / paired device.",
      "type" : "object",
      "properties" : {
        "status" : {
          "type": "integer"
        }
      },
      "required" : [ "status", "deviceState" ]
    }
  }
}

scala code:

// PairedDeviceFields.scala
case class PairedDeviceFields(status: BigInt)
object PairedDeviceFields {
  implicit val encodePairedDeviceFields = {
    val readOnlyKeys = Set[String]()
    Encoder.forProduct1("status") { (o: PairedDeviceFields) => o.status }.mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
  }
  implicit val decodePairedDeviceFields = Decoder.forProduct1("status")(PairedDeviceFields.apply _)
}

// PairedDeviceInfo.scala
case class PairedDeviceInfo(status: BigInt) extends PairedDeviceFields
object PairedDeviceInfo {
  implicit val encodePairedDeviceInfo = {
    val readOnlyKeys = Set[String]()
    Encoder.forProduct1("status") { (o: PairedDeviceInfo) => o.status }.mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
  }
  implicit val decodePairedDeviceInfo = Decoder.forProduct1("status")(PairedDeviceInfo.apply _)
}

I would expect either a type alias or some traits to be generated …

replicable example can be found here: https://github.com/tomasherman/guardrail-playground

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:31 (31 by maintainers)

github_iconTop GitHub Comments

5reactions
blast-hardcheesecommented, Apr 2, 2019

Well, a PairedDeviceInfo is a valid PairedDeviceFields in this respect.

While we can break that association, for something where Cat { allOf: [Mammal, Pet] }, a Cat should be able to be treated as either a Mammal or a Pet in the case of getExtendedPetInfo(pet: Pet) or getExtendedMammalInfo(mammal: Mammal)

My motivation for using Deferred is that it would remove superfluous classes, which is almost always better than generating shims and other unpleasantries (we’ve actually been through this before; Deferred replaced the type aliases and package objects that we initially supported).

The only downside I can see in your original case is that switching from

      "allOf" : [ {
        "$ref" : "#/definitions/PairedDeviceFields"
      } ]

to

      "allOf" : [ {
        "$ref" : "#/definitions/PairedDeviceFields"
      }, {
        "$ref" : "#/definitions/OtherStuff"
      } ]

would end up changing the type names and identifiers. Suddenly, foo: PairedDeviceFields would become PairedDeviceInfo, which would be the union of PairedDeviceFields and OtherStuff (however this ends up working, be it traits or otherwise.

Foregoing case classes entirely in favor of a strategy similar to what https://www.scala-sbt.org/contraband/ uses is another option. Each definition is either a trait or an interface where if a definition becomes tainted by polymorphism, the definition widens to become more broad.

This will slightly break encoders and decoders, but it would permit multiple-inheritance even in Java, at the expense of class literals and requiring constructors and accessors be generated. We’d also lose copy, which would impact consumers more than the guardrail code.

So, to recap (with emoji for voting):

  • 👍 Eliminate intermediate type definition, rolling up allOf: [A] to just A
  • 😀 Copy all members, breaking all type hierarchy for allOf’d types
  • 🎉 Switch to trait/interfaces, providing static convenience function members to compensate for not using case classes
0reactions
blast-hardcheesecommented, Apr 24, 2019

Question left in the PR

Read more comments on GitHub >

github_iconTop Results From Across the Web

Scala case class inheritance - Stack Overflow
What I would like to do is factor the common behaviour and fields in an ancestor case class and have the two actual...
Read more >
Case Class Inheritance - Knoldus Blogs
Now the question arises, how inheritance can be achieved in case classes. The answer is simple: Case Class can extend another Class, trait...
Read more >
Why was case-to-case inheritance removed from Scala? - Quora
Inheritance takes the code of the parent. It may shadow the parent's data , and it may override the parent's functions, but they...
Read more >
Case Class Inheritance | by Knoldus Inc. - Medium
Now the question arises, how inheritance can be achieved in case classes. The answer is simple: Case Class can extend another Class, trait...
Read more >
java - classes/inheritance, how do I handle special cases that ...
Another option might be to extract the algorithms in a class that implements an interface or abstract class. Then the calculation can then ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found