[FEATURE REQUEST] Constrained Primitive Types I do...
# arrow
i
[FEATURE REQUEST] Constrained Primitive Types I don't know if that was already requested but wanted to hear your thoughts on having
PositiveInt
,
NotBlankString
and other constrained primitives type in Arrow. • `PositiveDouble`: a value class that guarantees its value is positive and finite. • `NonNegativeDouble`: >= 0 and finite. • The same for
Int
,
Long
• `NotBlankString`: guarantees that a string is not blank •
NotBlankTrimmedString
like the above but also applies trimming •
Percent
: a float in [0f, 1f] • ... What are your thoughts? Apologies if that's already built and I haven't seen it. Sample implementation (incomplete): https://github.com/Ivy-Apps/ivy-wallet/tree/multiplatform/core/src/commonMain/kotlin/ivy/core/data/primitives
p
I like the idea, that is what value classes are for and how we got unsigned integers. I would like to add a StrictInt or ExactInt using Math.addExact for additions to throw on overflow. But it sounds like a general purpose separate library, I don't see how it relates to arrow. Although I personally would totally don't mind if it is part of arrow 🙂 It is hard to believe that there is no such a library already somewhere.
i
Yeah I agree with you. I haven't searched for such library but it would be pity if we don't have a well maintained one. Also being explicit and strict is a FP thing so IMO having it in Arrow won't hurt - like a separate package "arrow.datatypes" or whatever. The idea is that we already trust Arrow, it's well maintained and have an established community. Also a dozen of value classes + extensions and operator overloads won't make it more heavy
s
It's a concept that often comes back in FP, and I would be more than happy to create a new repository in the Arrow org and invite you all so we can collaborate and contribute there. I don't think it belongs in core, now that we've finally been able to cut down on binary size and API surface 😄 It can be considered "a general purpose separate library, but the goal of Arrow is not solely around "functional programming" but safer software in general. So it's more general purpose, we would be more than happy to facilitate and contribute to this ourselves. Are either of you interested in working / collaborating on this? I can create an new repository, and invite everyone that is interested on contributing there.
i
Yeah that sounds awesome! It'd be a honor for me to contribute to the Arrow project. Feel free to invite me to the repository. I'm having a full-time job and also maintain the Ivy Wallet project but would be more than happy to contribute to this in my free time arrow
c
@simon.vergauwen isn't this exactly the type refinement demo showcased by Arrow Meta a year or so ago?
s
@Iliyan Germanov, I'll create a repo where we can start building this this weekend. @CLOVIS Yes, kind-of but it's not yet in a state where it can be used in production 😞 It currently stands still due to lack of contributors, and maintainers and I guess instability with K2 not yet being released.
c
There's a talk next week at KotlinConf on K2 compiler plugins :)
s
Super excited, also getting super nervous for my talk 😱
p
On second thought, it might/should be arrow specific. The point of defining wrapper value classes for primitives is mostly to redefine operations like in case of unsigned integers. So it might be better to design them with Raise context receivers and maybe some nice DSL instead of just throwing like in my example with overflow.
s
I've been thinking of creating a nice validation library as well, with some specialized DSLs, etc. Perhaps this could fit nicely together in a single library. @Pavel Something similar to Konform, but built with
Raise
. https://github.com/konform-kt/konform
c
Maybe have the lib be vanilla Kotlin with all functions named
plusOrThrow
and create a companion Arrow compatibility layer that declares the real operators with using Raise? This way you still get the choice of using Arrow or not
@simon.vergauwen I'd be interested on that, and how well it can fit with Compose. Arrow feels like the missing piece of the puzzle
s
The 2.0 binary will be truly small, so including Arrow will be much less impactful.
c
It's not really about the binary size, it's about the mental model. Raise & co are great, but it's still quite a bit shift in thinking process (very similar to understanding suspend)
s
After context receivers, we can potentially even push it down into an even smaller binary as well.
c
I'm personally using them everywhere in personal projects, but at my day job where we're not even using coroutines yet, and most devs have no idea what FP is (and therefore are afraid of it 😔), Arrow is a lot to ask
p
@CLOVIS I am mainly thinking about primitive wrappers, so it will be operators, not functiones. Like
a + b
which throws/fails on integer overflow instead of giving incorrect result.
c
@Pavel I mean:
Copy code
// your-lib/src/...
value class Foo(...) {
  fun plusOrThrow(other: Foo): Foo
  fun plusOrNull(other: Foo): Foo?
}

// your-lib-arrow/src/...
context(Raise<...>)
operator fun Foo.plus(other: Foo): Foo
This way, users who don't want to use Arrow can (but have to be explicit about how they want failures), and users who like Arrow get the nicer codebase (= safer code is simpler to write)
p
@CLOVIS I see. Well, this is already offtopic and implementation detail 😉
i
My use-case for this is having types that provide certain guarantees. For example,
NotBlankTrimmedString
will always hold a string value that isn't blank and has no leading/trailing whitespaces. When I see:
Copy code
data class Person(val firstName: NotBlankTrimmedString, val lastName: NotBlankTrimmedString)
I know that I'll have valid firsName without doing validation if my criteria is not-blank AND trimmed. I want to be able to create those primitives in two ways: •
PositiveDouble(3.14)
(unsafe) - crashes on invalid •
PositiveDouble.fromDouble(a+b)
- returns Option<PositiveDouble> / Raise / whatever • Also: • Operator overloads for +, -, ... that are type safe •
.map
to map its value • PositiveDouble.toNonNegative() Example implementation (limited) https://github.com/Ivy-Apps/ivy-wallet/blob/multiplatform/core/src/commonMain/kotlin/ivy/core/data/primitives/PositiveDouble.kt Example for properties for different types: https://github.com/Ivy-Apps/ivy-wallet/tree/multiplatform/core/src/commonTest/kotlin/ivy/core/data/primitives Goals: • Guarantee that type the type invariant holds • Easy to create and use • No overhead
c
I agree with all you said except the syntax. The safe variant should be shorter to type and easier to find with auto-complete.
Bonus point if the unsafe variant makes it clear in its name
(PositiveDoubleOrThrow()
)
i
PositiveDouble.unsafe
seems more straightforward to me When @simon.vergauwen creates a GitHub we can have a more formal discussion there. I'm new to libraries so I'm happy with whatever process/ideas you have
c
Not a fan of
unsafe
because it doesn't communicate how it fails, and is connoted with memory safety. Probably bike shedding though.
s
Any preference on name? ChatGPT sugessted: • (Arrow) Refined? • Arrow Shield • Arrow Precision • Arrow Tighten
i
Arrow Shield and Arrow Precision are interesting but IMO something more self-explanatory would work better. It's hard to come up though. A few more suggestions: • Arrow Precise • Arrow Constrain • Arrow Primitives • Arrow Define • Arrow Strict Data Idk, names are hard I vote for Precision
• Arrow Guard • ArrowValidatedPrimitives • ArrowRefinedData • ArrowCheckedTypes • ArrowAssuredPrimitives
s
Guard, Precise, Exact are probably my favorites.
i
+1 for those, I like Exact
implementation("io.arrow-kt:arrow-exact:1.2.0-RC")
Looks nice! Let's see what others think, my favorite of the proposed ones is definitely Exact.
s
I created https://github.com/arrow-kt/arrow-exact, so we can start discussing & drafting some code there. I invited all three of you into the repo. If anyone wants to collaborate, feel free to fork or ask here so I can give you write access ☺️
i
Awesome! I won't be available tonight but will definitely check it out tomorrow evening. Excited to see it as a new dependency for Ivy Wallet arrow
s
No worries ☺️ Whenever you have time, no rush. Enjoy your weekend
i
I created this issue for discussion purposes. Apologies for the lack of formatting! Did it on my Fold. IMO, our best course of action is for everyone to share their ideas and decide on a scope/purpose of the library. Have a great weekend all! https://github.com/arrow-kt/arrow-exact/issues/4
y
Just dropping a link here to this YouTrack issue by Roman Elizarov about restricted types. Might be relevant to the design considerations of the library
s
Hey @Youssef Shoaib [MOD], Thanks for sharing! I saw this link, and it looks really interesting but I am curious how long it will be until such a feature makes it into the languages. FIR might also offer some solutions, but development and maintenance cost are currently still very high. I already upvote it a long time ago 😜 👍