types icon indicating copy to clipboard operation
types copied to clipboard

💡 The `create` and `createOrNull` factory functions

Open LVMVRQUXL opened this issue 1 year ago • 2 comments

📝 Description

Originally discussed in #335.

We would like to redesign our factory functions by introducing new create and createOrNull factory functions for creating our stable types with the following behavior:

  • create functions should throw an exception in case of invalid inputs
  • createOrNull functions should return null in case of invalid inputs.

Here's the goal of API that we should provide after completing this topic:

// In kotools.types.number package

fun StrictlyPositiveInt.Companion.create(number: Number): StrictlyPositiveInt
fun StrictlyPositiveInt.Companion.create(number: Number, message: (Number) -> Any): StrictlyPositiveInt
fun StrictlyPositiveInt.Companion.createOrNull(number: Number): StrictlyPositiveInt?

fun StrictlyNegativeInt.Companion.create(number: Number): StrictlyNegativeInt
fun StrictlyNegativeInt.Companion.create(number: Number, message: (Number) -> Any): StrictlyNegativeInt
fun StrictlyNegativeInt.Companion.createOrNull(number: Number): StrictlyNegativeInt?

fun PositiveInt.Companion.create(number: Number): PositiveInt
fun PositiveInt.Companion.create(number: Number, message: (Number) -> Any): PositiveInt
fun PositiveInt.Companion.createOrNull(number: Number): PositiveInt?

fun NegativeInt.Companion.create(number: Number): NegativeInt
fun NegativeInt.Companion.create(number: Number, message: (Number) -> Any): NegativeInt
fun NegativeInt.Companion.createOrNull(number: Number): NegativeInt?

fun NonZeroInt.Companion.create(number: Number): NonZeroInt
fun NonZeroInt.Companion.create(number: Number, message: (Number) -> Any): NonZeroInt
fun NonZeroInt.Companion.createOrNull(number: Number): NonZeroInt?

// In kotools.types.text package

fun NotBlankString.Companion.create(value: Any): NotBlankString
fun NotBlankString.Companion.create(value: Any, message: (Any) -> Any): NotBlankString
fun NotBlankString.Companion.createOrNull(value: Any): NotBlankString?

// In kotools.types.collection package

fun <E> NotEmptyList.Companion.create(collection: Collection<E>): NotEmptyList<E>
fun <E> NotEmptyList.Companion.create(collection: Collection<E>, message: (Collection<E>) -> Any): NotEmptyList<E>
fun <E> NotEmptyList.Companion.createOrNull(collection: Collection<E>): NotEmptyList<E>?
fun <E> NotEmptyList.Companion.of(head: E, vararg tail: E): NotEmptyList<E>

fun <E> NotEmptySet.Companion.create(collection: Collection<E>): NotEmptySet<E>
fun <E> NotEmptySet.Companion.create(collection: Collection<E>, message: (Collection<E>) -> Any): NotEmptySet<E>
fun <E> NotEmptySet.Companion.createOrNull(collection: Collection<E>): NotEmptySet<E>?
fun <E> NotEmptySet.Companion.of(head: E, vararg tail: E): NotEmptySet<E>

fun <K, V> NotEmptyMap.Companion.create(map: Map<K, V>): NotEmptyMap<K, V>
fun <K, V> NotEmptyMap.Companion.create(map: Map<K, V>, message: (Map<K, V>) -> Any): NotEmptyMap<K, V>
fun <K, V> NotEmptyMap.Companion.createOrNull(map: Map<K, V>): NotEmptyMap<K, V>?
fun <K, V> NotEmptyMap.Companion.of(head: Pair<K, V>, tail: Pair<K, V>): NotEmptyMap<K, V>

✅ Checklist

  • [ ] Wait for the completion of the issues below.
  • [ ] Close this topic as completed and update tracking issues if present.

For the StrictlyPositiveInt type:

  • [x] #342
  • [x] #597
  • [x] #438
  • [x] #440
  • [x] #442
  • [x] #443
  • [x] #444
  • [x] #445
  • [x] #446
  • [x] #447
  • [x] #448
  • [x] #449

For the StrictlyNegativeInt type:

  • [x] #347
  • [x] #598
  • [x] #439
  • [x] #441
  • [x] #450
  • [x] #451
  • [x] #452
  • [x] #453
  • [x] #454
  • [x] #455
  • [x] #456
  • [x] #457

For the PositiveInt type:

  • [x] #349
  • [x] #458
  • [x] #459
  • [x] #460
  • [x] #461
  • [x] #462
  • [x] #463
  • [x] #464
  • [x] #465
  • [x] #466
  • [ ] ✨ New PositiveInt.Companion.create function with custom message

For the NegativeInt type:

  • [x] #350
  • [x] #467
  • [x] #468
  • [x] #469
  • [x] #470
  • [x] #471
  • [x] #472
  • [x] #473
  • [x] #474
  • [x] #475
  • [ ] ✨ New NegativeInt.Companion.create function with custom message

For the NonZeroInt type:

  • [x] #351
  • [x] #476
  • [x] #477
  • [x] #478
  • [x] #479
  • [x] #480
  • [x] #481
  • [x] #482
  • [x] #483
  • [x] #484
  • [ ] ✨ New NonZeroInt.Companion.create function with custom message

For the NotBlankString type:

  • [x] #341
  • [x] #541
  • [x] #412
  • [x] #395
  • [x] #396
  • [x] #397
  • [x] #398
  • [x] #399
  • [x] #400
  • [x] #401
  • [x] #402
  • [x] #403
  • [ ] ✨ New NotBlankString.Companion.create function with custom message

For the NotEmptyList type:

  • [x] #352
  • [x] #497
  • [x] #498
  • [x] #499
  • [x] #500
  • [x] #501
  • [x] #502
  • [x] #503
  • [x] #504
  • [x] #505
  • [x] #506
  • [x] #507
  • [x] #508
  • [x] #509
  • [x] #510
  • [ ] ✨ New NotEmptyList.Companion.create function with custom message

For the NotEmptySet type:

  • [x] #353
  • [x] #511
  • [x] #512
  • [x] #513
  • [x] #514
  • [x] #515
  • [x] #516
  • [x] #517
  • [x] #518
  • [x] #519
  • [x] #520
  • [x] #521
  • [x] #522
  • [x] #523
  • [x] #524
  • [ ] ✨ New NotEmptySet.Companion.create function with custom message

For the NotEmptyMap type:

  • [x] #354
  • [x] #525
  • [x] #526
  • [x] #527
  • [x] #528
  • [x] #529
  • [x] #530
  • [x] #531
  • [x] #532
  • [x] #533
  • [x] #534
  • [ ] ✨ New NotEmptyMap.Companion.create function with custom message

LVMVRQUXL avatar Mar 17 '24 12:03 LVMVRQUXL

Like pointed out by the Kotlin community on Reddit, it may be useful to define an overload of the create functions accepting a custom message for the exception thrown in case of invalid input.

Here's an example for the PositiveInt type:

fun PositiveInt.Companion.create(number: Number, message: (Number) -> Any): PositiveInt

Here's an example of calling this function from Kotlin code:

val number: PositiveInt = PositiveInt.create(1) {
    "$it should be greater than or equal to zero."
}
println(number) // 1

Here's the same example of calling this function, but from Java code:

final PositiveInt result = PositiveInt.Companion.create(
    1,
    number -> number + " should be greater than or equal to zero."
);
System.out.println(result); // 1

If the specified message has a blank string representation, then it is ignored.

PositiveInt.create(-1) { "   " } // throws an IllegalArgumentException with a generic message

This idea not being essential for now, we should write it in a new discussion instead.

LVMVRQUXL avatar Mar 21 '24 00:03 LVMVRQUXL

Like suggested by @lbunschoten in https://github.com/kotools/types/discussions/335#discussioncomment-8904063, we could mark these functions with the JvmStatic annotation for improving user experience on Java. See the Kotlin documentation on that topic.

LVMVRQUXL avatar Mar 25 '24 23:03 LVMVRQUXL

Ideas like that should be documented in GitHub Discussions instead.

LVMVRQUXL avatar Jul 20 '24 09:07 LVMVRQUXL