Add operations on refined typed
Would be great to have a subset of the same operations available on unconstrained typed also available on refined types but tracking properties across operations. Even a small subset would probably already be a big win. I am thinking things like:
scala> val a: Int Refined Greater[W.`5`.T] = 10
scala> a + 1
res0: Int Refined Greater[W.`6`.T]
Are there any operations like this already? If not, adding a few to open the door for contributions in the same style might be a good way to get started.
:+1: As always, a typeclass-based approach would be the best approach.
:+1: Operations like these are not in the library yet, but they would be a welcome addition. It was brought up before on Gitter by @howyp who also started working on this - unfortunately I had not yet time look at it in detail. :-(
I'm not sure a constrained + unconstrained should gives us a constrained type.
Perhaps you mean constrained + literal ?
I agree this would be a great thing to have. I've done some of the ground-work for it, I'll see if I can get a PR raised (even if it's just for review rather than expecting a merge) in the next week or so.
@soronpo How about somethingWithKnownProperties + somethingElseWithKnownProperties should give somethingWithWhateverPropertiesWeCanDerive. Literals have known properties, refined types have known properties, any number type than Long also has some known limited ranges we know. We obviously do not have to aim for completeness right away.
@howyp sounds great :)!
IIRC there are issues here, for instance numerical types wrap from positive to negative (and/or vice versa).
I don't know refined well at all. Obviously we can only derive properties that can be represented in refined.
eg.
scala> Int.MaxValue
res0: Int = 2147483647
scala> Int.MaxValue + 1
res1: Int = -2147483648
With the addition of "companions" for predefined refined types there is now a suitable place for operations with these types. For example, I experimented with addition of NonNegInts here: https://github.com/fthomas/refined/blob/76d812c3508e45e6413914dd76a81908ae5cf1db/modules/core/shared/src/main/scala/eu/timepit/refined/types/numeric.scala#L19-L28
Which preserve the non-negative property even in case of overflows:
scala> NonNegInt.MaxValue
res1: eu.timepit.refined.types.all.NonNegInt = 2147483647
scala> NonNegInt.plus(NonNegInt.MaxValue, NonNegInt(1))
res2: eu.timepit.refined.types.all.NonNegInt = 0
scala> NonNegInt.plus(NonNegInt.MaxValue, NonNegInt(2))
res3: eu.timepit.refined.types.all.NonNegInt = 1