zio / zio-schema

Compositional, type-safe schema definitions, which enable auto-derivation of codecs and migrations.
https://zio.dev/zio-schema
Apache License 2.0
142 stars 162 forks source link

Scala 3.5 Warning Message Regarding Implicit Resolution #753

Open mikail-khan opened 1 week ago

mikail-khan commented 1 week ago

Given the following opaque type:

import scala.math.BigDecimal

import zio.*
import zio.json.*
import zio.prelude.{Equal, Validation}
import zio.schema.Schema

object Price {

  opaque type Price = BigDecimal

  // Factory method with Validation to ensure non-negative Price
  def make(amount: BigDecimal): Validation[Chunk[String], Price] =
    Validation.fromPredicateWith(Chunk("Price must be non-negative"))(amount)(_ >= 0)

  // JSON codec for Price using zio-json, with conversions to handle java.math.BigDecimal
  given JsonCodec[Price] = JsonCodec.bigDecimal.transformOrFail(
    amount => make(BigDecimal(amount)).toEither.left.map(_.mkString(", ")), // Handle Chunk errors
    price => unwrap(price).bigDecimal // Converts scala.math.BigDecimal to java.math.BigDecimal
  )

  // Extension method to unwrap the Price back to BigDecimal
  extension (price: Price) {
    def unwrap: BigDecimal = price
  }

  // Schema for Price using zio-schema, with validation to ensure non-negative
  given Schema[Price] =
    Schema[BigDecimal].transformOrFail(
      amount => make(amount).toEither.left.map(_.mkString(", ")), // Handle Chunk errors
      price => Right(unwrap(price))
    )

  // Equality instance for Price using zio.prelude
  given Equal[Price] = Equal.default

}

I receive the following compiler warning:

[warn] 40 |    Schema[BigDecimal].transformOrFail(
[warn]    |                      ^
[warn]    |Result of implicit search for zio.schema.Schema[BigDecimal] will change.
[warn]    |Current result given_Schema_Price will be no longer eligible
[warn]    |  because it is not defined before the search position.
[warn]    |Result with new rules: zio.schema.Schema.bigDecimal.
[warn]    |To opt into the new rules, compile with `-source future` or use
[warn]    |the `scala.language.future` language import.
[warn]    |
[warn]    |To fix the problem without the language import, you could try one of the following:
[warn]    |  - use a `given ... with` clause as the enclosing given,
[warn]    |  - rearrange definitions so that given_Schema_Price comes earlier,
[warn]    |  - use an explicit argument.
[warn]    |This will be an error in Scala 3.5 and later.

How do I future proof my code?

I tried to import zio.schema.Schema.bigDecimal but it does not get used.

I tried to replace the implicit definition as suggested in the warning message to:

  given Schema[Price] with {
    Schema[BigDecimal].transformOrFail(
      amount => make(amount).toEither.left.map(_.mkString(", ")), // Handle Chunk errors
      price => Right(unwrap(price))
    )
  }

but I get:

[error] 40 |  given Schema[Price] with {
[error]    |        ^
[error]    |        Cannot extend sealed trait Schema in a different source file
[error]    |
[error]    | longer explanation available when compiling with `-explain`
mikail-khan commented 1 week ago

One solution is to not use Scala's BigDecimal but Java's BigDecimal and then use Schema.primivitve

import zio.*
import zio.json.*
import zio.prelude.{Equal, Validation}
import zio.schema.Schema

import java.math.BigDecimal

object Price {

  opaque type Price = BigDecimal

  // Factory method with Validation to ensure non-negative Price
  def make(amount: BigDecimal): Validation[Chunk[String], Price] =
    Validation.fromPredicateWith(Chunk("Price must be non-negative"))(amount)((b: BigDecimal) =>
      b.compareTo(BigDecimal.ZERO) >= 0
    )

  // JSON codec for Price using zio-json, with conversions to handle java.math.BigDecimal
  given JsonCodec[Price] = JsonCodec.bigDecimal.transformOrFail(
    amount => make(amount).toEither.left.map(_.mkString(", ")), // Handle Chunk errors
    price => unwrap(price) // Converts scala.math.BigDecimal to java.math.BigDecimal
  )

  // Extension method to unwrap the Price back to BigDecimal
  extension (price: Price) {
    def unwrap: BigDecimal = price
  }

  // Schema for Price using zio-schema, with validation to ensure non-negative
  given Schema[Price] =
    Schema
      .primitive[BigDecimal]
      .transformOrFail(
        amount => make(amount).toEither.left.map(_.mkString(", ")), // Handle Chunk errors
        price => Right(unwrap(price))
      )

  // Equality instance for Price using zio.prelude
  given Equal[Price] = Equal.default

}
987Nabil commented 4 days ago

Try to add a implicit Schema of your need as an input for the given, so using Schema[BigDecimal] on the left hand side