• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 @file:Suppress("NOTHING_TO_INLINE", "SuspendCoroutine")
18 
19 package com.android.systemui.kairos.util
20 
21 import com.android.systemui.kairos.util.Maybe.Absent
22 import com.android.systemui.kairos.util.Maybe.Present
23 import kotlin.coroutines.Continuation
24 import kotlin.coroutines.CoroutineContext
25 import kotlin.coroutines.EmptyCoroutineContext
26 import kotlin.coroutines.RestrictsSuspension
27 import kotlin.coroutines.resume
28 import kotlin.coroutines.startCoroutine
29 import kotlin.coroutines.suspendCoroutine
30 
31 /** Represents a value that may or may not be present. */
32 sealed interface Maybe<out A> {
33     /** A [Maybe] value that is present. */
34     @JvmInline value class Present<out A> internal constructor(val value: A) : Maybe<A>
35 
36     /** A [Maybe] value that is not present. */
37     data object Absent : Maybe<Nothing>
38 
39     companion object {
40         /** Returns a [Maybe] containing [value]. */
41         fun <A> present(value: A): Maybe<A> = Present(value)
42 
43         /** A [Maybe] that is not present. */
44         val absent: Maybe<Nothing> = Absent
45 
46         /** A [Maybe] that is not present. */
47         inline fun <A> absent(): Maybe<A> = Absent
48     }
49 }
50 
51 /** Utilities to query [Maybe] instances from within a [maybe] block. */
52 @RestrictsSuspension
53 object MaybeScope {
notnull54     suspend operator fun <A> Maybe<A>.not(): A = suspendCoroutine { k ->
55         if (this is Present) k.resume(value)
56     }
57 
guardnull58     suspend inline fun guard(crossinline block: () -> Boolean): Unit = suspendCoroutine { k ->
59         if (block()) k.resume(Unit)
60     }
61 }
62 
63 /**
64  * Returns a [Maybe] value produced by evaluating [block].
65  *
66  * [block] can use its [MaybeScope] receiver to query other [Maybe] values, automatically cancelling
67  * execution of [block] and producing [Absent] when attempting to query a [Maybe] that is not
68  * present.
69  *
70  * This can be used instead of Kotlin's built-in nullability (`?.` and `?:`) operators when dealing
71  * with complex combinations of nullables:
72  * ```
73  * val aMaybe: Maybe<Any> = ...
74  * val bMaybe: Maybe<Any> = ...
75  * val result: String = maybe {
76  *   val a = !aMaybe
77  *   val b = !bMaybe
78  *   "Got: $a and $b"
79  * }
80  * ```
81  */
maybenull82 fun <A> maybe(block: suspend MaybeScope.() -> A): Maybe<A> {
83     var maybeResult: Maybe<A> = Absent
84     val k =
85         object : Continuation<A> {
86             override val context: CoroutineContext = EmptyCoroutineContext
87 
88             override fun resumeWith(result: Result<A>) {
89                 maybeResult = result.getOrNull()?.let { Maybe.present(it) } ?: Absent
90             }
91         }
92     block.startCoroutine(MaybeScope, k)
93     return maybeResult
94 }
95 
96 /** Returns a [Maybe] containing this value if it is not `null`. */
toMaybenull97 inline fun <A> (A?).toMaybe(): Maybe<A> = maybe(this)
98 
99 /** Returns a [Maybe] containing [value] if it is not `null`. */
100 inline fun <A> maybe(value: A?): Maybe<A> = value?.let { Maybe.present(it) } ?: Absent
101 
102 /** Returns a [Maybe] that is absent. */
maybeOfnull103 fun <A> maybeOf(): Maybe<A> = Absent
104 
105 /** Returns a [Maybe] containing [value]. */
106 fun <A> maybeOf(value: A): Maybe<A> = Present(value)
107 
108 /** Returns the value present in this [Maybe], or `null` if not present. */
109 inline fun <A> Maybe<A>.orNull(): A? = orElse(null)
110 
111 /**
112  * Returns a [Maybe] holding the result of applying [transform] to the value in the original
113  * [Maybe].
114  */
115 inline fun <A, B> Maybe<A>.map(transform: (A) -> B): Maybe<B> =
116     when (this) {
117         is Present -> Maybe.present(transform(value))
118         is Absent -> Absent
119     }
120 
121 /** Returns the result of applying [transform] to the value in the original [Maybe]. */
flatMapnull122 inline fun <A, B> Maybe<A>.flatMap(transform: (A) -> Maybe<B>): Maybe<B> =
123     when (this) {
124         is Present -> transform(value)
125         is Absent -> Absent
126     }
127 
128 /** Returns the value present in this [Maybe], or the result of [defaultValue] if not present. */
orElseGetnull129 inline fun <A> Maybe<A>.orElseGet(defaultValue: () -> A): A =
130     when (this) {
131         is Present -> value
132         is Absent -> defaultValue()
133     }
134 
135 /**
136  * Returns the value present in this [Maybe], or invokes [error] with the message returned from
137  * [getMessage].
138  */
<lambda>null139 inline fun <A> Maybe<A>.orError(getMessage: () -> Any): A = orElseGet { error(getMessage()) }
140 
141 /** Returns the value present in this [Maybe], or [defaultValue] if not present. */
orElsenull142 inline fun <A> Maybe<A>.orElse(defaultValue: A): A =
143     when (this) {
144         is Present -> value
145         is Absent -> defaultValue
146     }
147 
148 /**
149  * Returns a [Maybe] that contains the present in the original [Maybe], only if it satisfies
150  * [predicate].
151  */
filternull152 inline fun <A> Maybe<A>.filter(predicate: (A) -> Boolean): Maybe<A> =
153     when (this) {
154         is Present -> if (predicate(value)) this else Absent
155         else -> this
156     }
157 
158 /** Returns a [List] containing all values that are present in this [Iterable]. */
filterPresentnull159 fun <A> Iterable<Maybe<A>>.filterPresent(): List<A> = asSequence().filterPresent().toList()
160 
161 /** Returns a [List] containing all values that are present in this [Sequence]. */
162 fun <A> Sequence<Maybe<A>>.filterPresent(): Sequence<A> =
163     filterIsInstance<Present<A>>().map { it.value }
164 
165 // Align
166 
167 /**
168  * Returns a [Maybe] containing the result of applying the values present in the original [Maybe]
169  * and other, applied to [transform] as a [These].
170  */
alignWithnull171 inline fun <A, B, C> Maybe<A>.alignWith(other: Maybe<B>, transform: (These<A, B>) -> C): Maybe<C> =
172     when (this) {
173         is Present -> {
174             val a = value
175             when (other) {
176                 is Present -> {
177                     val b = other.value
178                     Maybe.present(transform(These.both(a, b)))
179                 }
180 
181                 Absent -> Maybe.present(transform(These.first(a)))
182             }
183         }
184         Absent ->
185             when (other) {
186                 is Present -> {
187                     val b = other.value
188                     Maybe.present(transform(These.second(b)))
189                 }
190 
191                 Absent -> Maybe.absent
192             }
193     }
194 
195 // Alt
196 
197 /** Returns a [Maybe] containing the value present in the original [Maybe], or [other]. */
<lambda>null198 infix fun <A> Maybe<A>.orElseMaybe(other: Maybe<A>): Maybe<A> = orElseGetMaybe { other }
199 
200 /**
201  * Returns a [Maybe] containing the value present in the original [Maybe], or the result of [other].
202  */
orElseGetMaybenull203 inline fun <A> Maybe<A>.orElseGetMaybe(other: () -> Maybe<A>): Maybe<A> =
204     when (this) {
205         is Present -> this
206         else -> other()
207     }
208 
209 // Apply
210 
211 /**
212  * Returns a [Maybe] containing the value present in [argMaybe] applied to the function present in
213  * the original [Maybe].
214  */
applynull215 fun <A, B> Maybe<(A) -> B>.apply(argMaybe: Maybe<A>): Maybe<B> = flatMap { f ->
216     argMaybe.map { a -> f(a) }
217 }
218 
219 /**
220  * Returns a [Maybe] containing the result of applying [transform] to the values present in the
221  * original [Maybe] and [other].
222  */
anull223 inline fun <A, B, C> Maybe<A>.zipWith(other: Maybe<B>, transform: (A, B) -> C) = flatMap { a ->
224     other.map { b -> transform(a, b) }
225 }
226 
227 // Bind
228 
229 /**
230  * Returns a [Maybe] containing the value present in the [Maybe] present in the original [Maybe].
231  */
flattennull232 fun <A> Maybe<Maybe<A>>.flatten(): Maybe<A> = flatMap { it }
233 
234 // Semigroup
235 
236 /**
237  * Returns a [Maybe] containing the result of applying the values present in the original [Maybe]
238  * and other, applied to [transform].
239  */
mergeWithnull240 fun <A> Maybe<A>.mergeWith(other: Maybe<A>, transform: (A, A) -> A): Maybe<A> =
241     alignWith(other) { it.merge(transform) }
242 
243 /**
244  * Returns a list containing only the present results of applying [transform] to each element in the
245  * original iterable.
246  */
<lambda>null247 inline fun <A, B> Iterable<A>.mapMaybe(transform: (A) -> Maybe<B>): List<B> = buildList {
248     for (a in this@mapMaybe) {
249         val result = transform(a)
250         if (result is Present) {
251             add(result.value)
252         }
253     }
254 }
255 
256 /**
257  * Returns a sequence containing only the present results of applying [transform] to each element in
258  * the original sequence.
259  */
mapMaybenull260 fun <A, B> Sequence<A>.mapMaybe(transform: (A) -> Maybe<B>): Sequence<B> =
261     map(transform).filterIsInstance<Present<B>>().map { it.value }
262 
263 /**
264  * Returns a map with values of only the present results of applying [transform] to each entry in
265  * the original map.
266  */
mapMaybeValuesnull267 inline fun <K, A, B> Map<K, A>.mapMaybeValues(transform: (Map.Entry<K, A>) -> Maybe<B>): Map<K, B> =
268     buildMap {
269         for (entry in this@mapMaybeValues) {
270             val result = transform(entry)
271             if (result is Present) {
272                 put(entry.key, result.value)
273             }
274         }
275     }
276 
277 /** Returns a map with all non-present values filtered out. */
filterPresentValuesnull278 fun <K, A> Map<K, Maybe<A>>.filterPresentValues(): Map<K, A> =
279     asSequence().mapMaybe { (key, mValue) -> mValue.map { key to it } }.toMap()
280 
281 /**
282  * Returns a pair of [Maybes][Maybe] that contain the [Pair.first] and [Pair.second] values present
283  * in the original [Maybe].
284  */
splitPairnull285 fun <A, B> Maybe<Pair<A, B>>.splitPair(): Pair<Maybe<A>, Maybe<B>> =
286     map { it.first } to map { it.second }
287 
288 /** Returns the value associated with [key] in this map as a [Maybe]. */
getMaybenull289 fun <K, V> Map<K, V>.getMaybe(key: K): Maybe<V> {
290     val value = get(key)
291     if (value == null && !containsKey(key)) {
292         return Maybe.absent
293     } else {
294         @Suppress("UNCHECKED_CAST")
295         return Maybe.present(value as V)
296     }
297 }
298