arrow-integrations icon indicating copy to clipboard operation
arrow-integrations copied to clipboard

Add support for generic tristate (undefined | null | defined) codec

Open myuwono opened this issue 2 years ago • 2 comments

Feature Request

  • add support for serialization / deseiralization of TriState<A> which models Absent | Null | Defined<A>

Context

There are times where users need to model an API that need differentiate between:

  • absence of a field
  • presence of a field in json, but being set to null
  • presence of a field in json, with a value set

An example is a PATCH endpoint that may have the following contract:

// modify nickname
PATCH /api/user/{id} { "nickname" : "john doe" }

// deletes nickname
PATCH /api/user/{id} { "nickname" : null }

// does nothing
PATCH /api/user/{id} { }

Currently nullables T? is not a viable alternative as they are missing a degree of freedom (the absence) and hence isn't usable. As well, Option<T> is generally used to treat missing and absence as None by convention. While Option<T?> is likely to be a possible container type for this, it is somewhat not feasible to be used due to null being a synthetic type. I.e. somehow Jackson could not differentiate whether the type signature requested is nullable or not at runtime, or at least I could not find a way to do so with my limited knowledge.

A possible alternative would be to define a TriState<A> as follows:

sealed class TriState<out A> {
  companion object

  object Absent : TriState<Nothing>()
  object Null : TriState<Nothing>()
  data class Defined<T>(val value: T) : TriState<T>()
}

Given the code above we'd have a concrete distinction between Absent or Null that Jackson can use. This makes modelling the above behaviour possible. Additionally, this model can be bi-directionally converted to Arrow's Option<T?> or Option<Option<T>> without loss of precision which hopefully should support the ergonomic and integration with the rest of the arrow ecosystem.

Another alternative would be to expose a module that users can use to implement such sealed class within their own codebase e.g. GenericTriStateModule<T>.

myuwono avatar Nov 26 '22 13:11 myuwono

Hello @myuwono, I just quickly read this issue and that make me think of jackson-databind-nullable. I don’t know if you already check this module but perhaps it can help.

christophejan avatar Dec 11 '22 23:12 christophejan

Thank you @christophejan I wasn't aware of that project. That one is interesting. JsonNullable<T> does have the similar objective, approached using using the Java Optional-ish approach instead of Kotlin sealed class. I was initially on the fence whether or not a new type would be necessary. After looking into that library, it appears that a new dedicated type would indeed be a good addition to arrow-integrations-jackson. We will need a good name for it.

myuwono avatar Dec 14 '22 11:12 myuwono