Skip to content

Commit c5aaf63

Browse files
Tavish Pegramtapegram
Tavish Pegram
andauthored
Add nullable fns to replace option fns (#191)
Co-authored-by: Tavish Pegram <[email protected]>
1 parent f15701f commit c5aaf63

File tree

4 files changed

+162
-4
lines changed

4 files changed

+162
-4
lines changed

arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/core/Either.kt

+21
Original file line numberDiff line numberDiff line change
@@ -799,9 +799,30 @@ sealed class Either<out A, out B> : EitherOf<A, B> {
799799
* Left(12).toOption() // Result: None
800800
* ```
801801
*/
802+
@Deprecated("Deprecated, use `orNull` instead", ReplaceWith("orNull()"))
802803
fun toOption(): Option<B> =
803804
fold({ None }, { Some(it) })
804805

806+
/**
807+
* Returns the right value if it exists, otherwise null
808+
*
809+
* Example:
810+
* ```kotlin:ank:playground
811+
* import arrow.core.Right
812+
* import arrow.core.Left
813+
*
814+
* //sampleStart
815+
* val right = Right(12).orNull() // Result: 12
816+
* val left = Left(12).orNull() // Result: null
817+
* //sampleEnd
818+
* fun main() {
819+
* println("right = $right")
820+
* println("left = $left")
821+
* }
822+
* ```
823+
*/
824+
fun orNull(): B? = fold({ null }, { it })
825+
805826
/**
806827
* The left side of the disjoint union, as opposed to the [Right] side.
807828
*/

arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/core/Ior.kt

+101-4
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ sealed class Ior<out A, out B> : IorOf<A, B> {
7676
* @return [None] if both [oa] and [ob] are [None]. Otherwise [Some] wrapping
7777
* an [Ior.Left], [Ior.Right], or [Ior.Both] if [oa], [ob], or both are defined (respectively).
7878
*/
79-
79+
@Deprecated("Deprecated, use `fromNullables` instead", ReplaceWith("fromNullables(a, b)"))
8080
fun <A, B> fromOptions(oa: Option<A>, ob: Option<B>): Option<Ior<A, B>> = when (oa) {
8181
is Some -> when (ob) {
8282
is Some -> Some(Both(oa.t, ob.t))
@@ -88,6 +88,27 @@ sealed class Ior<out A, out B> : IorOf<A, B> {
8888
}
8989
}
9090

91+
/**
92+
* Create an [Ior] from two nullables if at least one of them is defined.
93+
*
94+
* @param a an element (nullable) for the left side of the [Ior]
95+
* @param b an element (nullable) for the right side of the [Ior]
96+
*
97+
* @return [null] if both [a] and [b] are [null]. Otherwise
98+
* an [Ior.Left], [Ior.Right], or [Ior.Both] if [a], [b], or both are defined (respectively).
99+
*/
100+
fun <A, B> fromNullables(a: A?, b: B?): Ior<A, B>? =
101+
when (a != null) {
102+
true -> when (b != null) {
103+
true -> Both(a, b)
104+
false -> Left(a)
105+
}
106+
false -> when (b != null) {
107+
true -> Right(b)
108+
false -> null
109+
}
110+
}
111+
91112
private tailrec fun <L, A, B> Semigroup<L>.loop(v: Ior<L, Either<A, B>>, f: (A) -> IorOf<L, Either<A, B>>): Ior<L, B> = when (v) {
92113
is Left -> Left(v.value)
93114
is Right -> when (v.value) {
@@ -235,17 +256,44 @@ sealed class Ior<out A, out B> : IorOf<A, B> {
235256
*
236257
* Example:
237258
* ```
238-
* Right(12).pad() // Result: Pair(None, Some(12))
239-
* Left(12).pad() // Result: Pair(Some(12), None)
240-
* Both("power", 12).pad() // Result: Pair(Some("power"), Some(12))
259+
* Ior.Right(12).pad() // Result: Pair(None, Some(12))
260+
* Ior.Left(12).pad() // Result: Pair(Some(12), None)
261+
* Ior.Both("power", 12).pad() // Result: Pair(Some("power"), Some(12))
241262
* ```
242263
*/
264+
@Deprecated("Deprecated, use `padNull` instead", ReplaceWith("padNull()"))
243265
fun pad(): Pair<Option<A>, Option<B>> = fold(
244266
{ Pair(Some(it), None) },
245267
{ Pair(None, Some(it)) },
246268
{ a, b -> Pair(Some(a), Some(b)) }
247269
)
248270

271+
/**
272+
* Return this [Ior] as [Pair] of nullables]
273+
*
274+
* Example:
275+
* ```kotlin:ank:playground
276+
* import arrow.core.Ior
277+
*
278+
* //sampleStart
279+
* val right = Ior.Right(12).padNull() // Result: Pair(null, 12)
280+
* val left = Ior.Left(12).padNull() // Result: Pair(12, null)
281+
* val both = Ior.Both("power", 12).padNull() // Result: Pair("power", 12)
282+
* //sampleEnd
283+
*
284+
* fun main() {
285+
* println("right = $right")
286+
* println("left = $left")
287+
* println("both = $both")
288+
* }
289+
* ```
290+
*/
291+
fun padNull(): Pair<A?, B?> = fold(
292+
{ Pair(it, null) },
293+
{ Pair(null, it) },
294+
{ a, b -> Pair(a, b) }
295+
)
296+
249297
/**
250298
* Returns a [Either.Right] containing the [Right] value or `B` if this is [Right] or [Both]
251299
* and [Either.Left] if this is a [Left].
@@ -271,9 +319,33 @@ sealed class Ior<out A, out B> : IorOf<A, B> {
271319
* Both(12, "power").toOption() // Result: Some("power")
272320
* ```
273321
*/
322+
@Deprecated("Deprecated, use `orNull` instead", ReplaceWith("orNull()"))
274323
fun toOption(): Option<B> =
275324
fold({ None }, { Some(it) }, { _, b -> Some(b) })
276325

326+
/**
327+
* Returns the [Right] value or `B` if this is [Right] or [Both]
328+
* and [null] if this is a [Left].
329+
*
330+
* Example:
331+
* ```kotlin:ank:playground
332+
* import arrow.core.Ior
333+
*
334+
* //sampleStart
335+
* val right = Ior.Right(12).orNull() // Result: 12
336+
* val left = Ior.Left(12).orNull() // Result: null
337+
* val both = Ior.Both(12, "power").orNull() // Result: "power"
338+
* //sampleEnd
339+
* fun main() {
340+
* println("right = $right")
341+
* println("left = $left")
342+
* println("both = $both")
343+
* }
344+
* ```
345+
*/
346+
fun orNull(): B? =
347+
fold({ null }, { it }, { _, b -> b })
348+
277349
/**
278350
* Returns a [Some] containing the [Left] value or `A` if this is [Left] or [Both]
279351
* and [None] if this is a [Right].
@@ -285,9 +357,34 @@ sealed class Ior<out A, out B> : IorOf<A, B> {
285357
* Both(12, "power").toLeftOption() // Result: Some(12)
286358
* ```
287359
*/
360+
@Deprecated("Deprecated, use `leftOrNull` instead", ReplaceWith("leftOrNull()"))
288361
fun toLeftOption(): Option<A> =
289362
fold({ Option.just(it) }, { Option.empty() }, { a, _ -> Option.just(a) })
290363

364+
/**
365+
* Returns the [Left] value or `A` if this is [Left] or [Both]
366+
* and [null] if this is a [Right].
367+
*
368+
* Example:
369+
* ```kotlin:ank:playground
370+
* import arrow.core.Ior
371+
*
372+
* //sampleStart
373+
* val right = Ior.Right(12).leftOrNull() // Result: null
374+
* val left = Ior.Left(12).leftOrNull() // Result: 12
375+
* val both = Ior.Both(12, "power").leftOrNull() // Result: 12
376+
* //sampleEnd
377+
*
378+
* fun main() {
379+
* println("right = $right")
380+
* println("left = $left")
381+
* println("both = $both")
382+
* }
383+
* ```
384+
*/
385+
fun leftOrNull(): A? =
386+
fold({ it }, { null }, { a, _ -> a })
387+
291388
/**
292389
* Returns a [Validated.Valid] containing the [Right] value or `B` if this is [Right] or [Both]
293390
* and [Validated.Invalid] if this is a [Left].

arrow-libs/core/arrow-core-data/src/test/kotlin/arrow/core/EitherTest.kt

+7
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ class EitherTest : UnitSpec() {
187187
}
188188
}
189189

190+
"orNull should convert" {
191+
forAll { a: Int ->
192+
Right(a).orNull() == a &&
193+
Left(a).orNull() == null
194+
}
195+
}
196+
190197
"contains should check value" {
191198
forAll(Gen.intSmall(), Gen.intSmall()) { a: Int, b: Int ->
192199
Right(a).contains(a) &&

arrow-libs/core/arrow-core-data/src/test/kotlin/arrow/core/IorTest.kt

+33
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ class IorTest : UnitSpec() {
112112
}
113113
}
114114

115+
"padNull() should return the correct Pair of nullables" {
116+
forAll { a: Int, b: String ->
117+
Ior.Left(a).padNull() == Pair(a, null) &&
118+
Ior.Right(b).padNull() == Pair(null, b) &&
119+
Ior.Both(a, b).padNull() == Pair(a, b)
120+
}
121+
}
122+
115123
"toEither() should convert values into a valid Either" {
116124
forAll { a: Int, b: String ->
117125
Ior.Left(a).toEither() == Either.Left(a) &&
@@ -128,6 +136,22 @@ class IorTest : UnitSpec() {
128136
}
129137
}
130138

139+
"orNull() should convert right values into a nullable" {
140+
forAll { a: Int, b: String ->
141+
Ior.Left(a).orNull() == null &&
142+
Ior.Right(b).orNull() == b &&
143+
Ior.Both(a, b).orNull() == b
144+
}
145+
}
146+
147+
"leftOrNull() should convert left values into a nullable" {
148+
forAll { a: Int, b: String ->
149+
Ior.Left(a).leftOrNull() == a &&
150+
Ior.Right(b).leftOrNull() == null &&
151+
Ior.Both(a, b).leftOrNull() == a
152+
}
153+
}
154+
131155
"toValidated() should convert values into a valid Validated" {
132156
forAll { a: Int, b: String ->
133157
Ior.Left(a).toValidated() == Invalid(a) &&
@@ -145,6 +169,15 @@ class IorTest : UnitSpec() {
145169
}
146170
}
147171

172+
"fromNullables() should build a correct Ior" {
173+
forAll { a: Int, b: String ->
174+
Ior.fromNullables(a, null) == Ior.Left(a) &&
175+
Ior.fromNullables(a, b) == Ior.Both(a, b) &&
176+
Ior.fromNullables(null, b) == Ior.Right(b) &&
177+
Ior.fromNullables(null, null) == null
178+
}
179+
}
180+
148181
"getOrElse() should return value" {
149182
forAll { a: Int, b: Int ->
150183
Ior.Right(a).getOrElse { b } == a &&

0 commit comments

Comments
 (0)