|
| 1 | +## [1.9.0](https://github.com/kevin-lee/refined4s/issues?q=is%3Aissue%20is%3Aclosed%20-label%3Ainvalid%20-label%3Awontfix%20milestone%3Am30) - 2025-09-05 |
| 2 | + |
| 3 | +### New Features |
| 4 | + |
| 5 | +* [`refined4s-core`] Add `cats.Hash` type class instances for `refined4s.types.strings` with `orphan-cats` (#488) |
| 6 | +* [`refined4s-core`] Add `cats.Hash` type class instances for `refined4s.types.network` with `orphan-cats` (#490) |
| 7 | +* [`refined4s-core`] Add `cats.Hash` type class instances for `refined4s.types.numeric` with `orphan-cats` (#491) |
| 8 | +* [`refined4s-core`] Add `cats.Hash` type class instances for `refined4s.types.time` with `orphan-cats` (#492) |
| 9 | +* [`refined4s-circe`] Add `Encoder`, `Decoder`, `KeyEncoder` and `KeyDecoder` type-class instances for `refined4s.types.time` types (#497) |
| 10 | + |
| 11 | + Add `Encoder`, `Decoder`, `KeyEncoder` and `KeyDecoder` instances for |
| 12 | + * `Month` |
| 13 | + * `Day` |
| 14 | + * `Hour` |
| 15 | + * `Minute` |
| 16 | + * `Second` |
| 17 | + * `Millis` |
| 18 | + |
| 19 | + |
| 20 | +* [`refined4s-extras-render`] Add `Render` type-class instances for `refined4s.types.time` types (#499) |
| 21 | +* [`refined4s-chimney`] Add `Transformer` and `PartialTransformer` type-class instances for `refined4s.types.time` types (#500) |
| 22 | +* [`refined4s-pureconfig`] Add `ConfigReader` and `ConfigWriter` type-class instances for `refined4s.types.time` types (#501) |
| 23 | +* [`refined4s-doobie-ce2`] Add `Get` and `Put` type-class instances for `refined4s.types.time` types (#502) |
| 24 | +* [`refined4s-doobie-ce3`] Add `Get` and `Put` type-class instances for `refined4s.types.time` types (#503) |
| 25 | +* [`refined4s-tapir`] Add `Schema` type-class instances for `refined4s.types.time` types (#504) |
| 26 | +* [`refined4s-cats`] Add `CatsHash` to derive `Hash` from the actual type's `Hash` (#508) |
| 27 | + |
| 28 | + ```scala 3 |
| 29 | + type MyNewtype = MyNewtype.Type |
| 30 | + object MyNewtype extends Newtype[String], CatsHash[String] |
| 31 | + |
| 32 | + type MyRefinedType = MyRefinedType.Type |
| 33 | + object MyRefinedType extends Refined[String], CatsHash[String] { |
| 34 | + override inline def invalidReason(a: String): String = |
| 35 | + "It has to be a non-empty String but got \"" + a + "\"" |
| 36 | + |
| 37 | + override inline def predicate(a: String): Boolean = a != "" |
| 38 | + } |
| 39 | + |
| 40 | + type MyRefinedNewtype = MyRefinedNewtype.Type |
| 41 | + object MyRefinedNewtype extends Newtype[MyRefinedType], CatsHash[MyRefinedType] |
| 42 | + ``` |
| 43 | + |
| 44 | +*** |
| 45 | + |
| 46 | +### Internal Changes |
| 47 | + |
| 48 | +* Do not use `java.net.URL`'s constructors as they are deprecated since Java 20 (#484) |
| 49 | + |
| 50 | + `new URL()` is used in `Url` validation, but it shouldn't be used because it was deprecated in Java 20. |
| 51 | + |
| 52 | + |
| 53 | +#### Reorganize type-class instances in the modules (#507) |
| 54 | +1. `all` => `strings`, `numeric`, `network` and `time` + `all` |
| 55 | + |
| 56 | + If the type class instances are in the `all` object, split them into multiple objects such as `strings`, `numeric`, `network`, and `time`, as well as an `all` object that contains all of them. |
| 57 | + |
| 58 | +2. Move type-class instances from `trait`s to `object`s |
| 59 | + |
| 60 | + The type-class instances in `trait`s should be moved to `object`s and the `trait`s should have the same ones from the `object`s. |
| 61 | + |
| 62 | + e.g.) |
| 63 | + ```scala 3 |
| 64 | + trait strings { |
| 65 | + |
| 66 | + /* NonEmptyString */ |
| 67 | + inline given derivedNonEmptyStringEncoder: Encoder[NonEmptyString] = Encoder[String].contramap[NonEmptyString](_.value) |
| 68 | + inline given derivedNonEmptyStringDecoder: Decoder[NonEmptyString] = Decoder[String].emap(NonEmptyString.from) |
| 69 | + } |
| 70 | + object strings extends strings |
| 71 | + ``` |
| 72 | + to |
| 73 | + ```scala 3 |
| 74 | + trait strings { |
| 75 | + |
| 76 | + /* NonEmptyString */ |
| 77 | + inline given derivedNonEmptyStringEncoder: Encoder[NonEmptyString] = strings.derivedNonEmptyStringEncoder |
| 78 | + inline given derivedNonEmptyStringDecoder: Decoder[NonEmptyString] = strings.derivedNonEmptyStringDecoder |
| 79 | + |
| 80 | + } |
| 81 | + object strings { |
| 82 | + |
| 83 | + /* NonEmptyString */ |
| 84 | + given derivedNonEmptyStringEncoder: Encoder[NonEmptyString] = Encoder[String].contramap[NonEmptyString](_.value) |
| 85 | + given derivedNonEmptyStringDecoder: Decoder[NonEmptyString] = Decoder[String].emap(NonEmptyString.from) |
| 86 | + |
| 87 | + } |
| 88 | + ``` |
| 89 | + |
| 90 | + |
| 91 | +##### Why? |
| 92 | +With |
| 93 | +```scala 3 |
| 94 | +trait strings { |
| 95 | + |
| 96 | + /* NonEmptyString */ |
| 97 | + inline given derivedNonEmptyStringEncoder: Encoder[NonEmptyString] = Encoder[String].contramap[NonEmptyString](_.value) |
| 98 | + inline given derivedNonEmptyStringDecoder: Decoder[NonEmptyString] = Decoder[String].emap(NonEmptyString.from) |
| 99 | +} |
| 100 | +object strings extends strings |
| 101 | +``` |
| 102 | +Each object extending the `trait` has new non-equal type-class instances, and it is completely unnecessary. |
| 103 | +```scala 3 |
| 104 | +val strings = new refined4s.modules.circe.derivation.types.strings {} |
| 105 | + |
| 106 | +strings.derivedNonEmptyStringEncoder == refined4s.modules.circe.derivation.types.strings.derivedNonEmptyStringEncoder |
| 107 | +// Boolean = false |
| 108 | + |
| 109 | +strings.derivedNonEmptyStringEncoder eq refined4s.modules.circe.derivation.types.strings.derivedNonEmptyStringEncoder |
| 110 | +// Boolean = false |
| 111 | +``` |
| 112 | +*** |
| 113 | +With |
| 114 | +```scala 3 |
| 115 | +trait strings { |
| 116 | + |
| 117 | + /* NonEmptyString */ |
| 118 | + inline given derivedNonEmptyStringEncoder: Encoder[NonEmptyString] = strings.derivedNonEmptyStringEncoder |
| 119 | + inline given derivedNonEmptyStringDecoder: Decoder[NonEmptyString] = strings.derivedNonEmptyStringDecoder |
| 120 | + |
| 121 | +} |
| 122 | +object strings { |
| 123 | + |
| 124 | + /* NonEmptyString */ |
| 125 | + given derivedNonEmptyStringEncoder: Encoder[NonEmptyString] = Encoder[String].contramap[NonEmptyString](_.value) |
| 126 | + given derivedNonEmptyStringDecoder: Decoder[NonEmptyString] = Decoder[String].emap(NonEmptyString.from) |
| 127 | + |
| 128 | +} |
| 129 | +``` |
| 130 | +It is |
| 131 | +```scala 3 |
| 132 | +val strings = new refined4s.modules.circe.derivation.types.strings {} |
| 133 | + |
| 134 | +strings.derivedNonEmptyStringEncoder == refined4s.modules.circe.derivation.types.strings.derivedNonEmptyStringEncoder |
| 135 | +// Boolean = true |
| 136 | + |
| 137 | +strings.derivedNonEmptyStringEncoder eq refined4s.modules.circe.derivation.types.strings.derivedNonEmptyStringEncoder |
| 138 | +// Boolean = true |
| 139 | +``` |
| 140 | + |
| 141 | +*** |
| 142 | + |
| 143 | +### Internal Housekeeping |
| 144 | + |
| 145 | +* [`refined4s-cats`] Add not-equal tests for `Eq` instances for the types in `refined4s.types` (#486) |
0 commit comments