1 /*
2 * 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 package com.android.systemui.kairos
18
19 import com.android.systemui.kairos.internal.CompletableLazy
20 import com.android.systemui.kairos.internal.InitScope
21 import com.android.systemui.kairos.internal.NoScope
22 import com.android.systemui.kairos.internal.TransactionalImpl
23 import com.android.systemui.kairos.internal.init
24 import com.android.systemui.kairos.internal.transactionalImpl
25 import com.android.systemui.kairos.internal.util.hashString
26
27 /**
28 * A time-varying value. A [Transactional] encapsulates the idea of some continuous state; each time
29 * it is "sampled", a new result may be produced.
30 *
31 * Because Kairos operates over an "idealized" model of Time that can be passed around as a data
32 * type, [Transactionals][Transactional] are guaranteed to produce the same result if queried
33 * multiple times at the same (conceptual) time, in order to preserve _referential transparency_.
34 */
35 @ExperimentalKairosApi
36 class Transactional<out A> internal constructor(internal val impl: State<TransactionalImpl<A>>) {
toStringnull37 override fun toString(): String = "${this::class.simpleName}@$hashString"
38 }
39
40 /** A constant [Transactional] that produces [value] whenever it is sampled. */
41 @ExperimentalKairosApi
42 fun <A> transactionalOf(value: A): Transactional<A> =
43 Transactional(stateOf(TransactionalImpl.Const(CompletableLazy(value))))
44
45 /**
46 * Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
47 * this [DeferredValue].
48 *
49 * When the returned [Transactional] is accessed by the Kairos network, the [DeferredValue] will be
50 * queried and used.
51 *
52 * Useful for recursive definitions.
53 *
54 * ```
55 * fun <A> DeferredValue<Transactional<A>>.defer() = deferredTransactional { get() }
56 * ```
57 */
58 @ExperimentalKairosApi
59 fun <A> DeferredValue<Transactional<A>>.defer(): Transactional<A> = deferInline { unwrapped.value }
60
61 /**
62 * Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
63 * this [Lazy].
64 *
65 * When the returned [Transactional] is accessed by the Kairos network, the [Lazy]'s
66 * [value][Lazy.value] will be queried and used.
67 *
68 * Useful for recursive definitions.
69 *
70 * ```
71 * fun <A> Lazy<Transactional<A>>.defer() = deferredTransactional { value }
72 * ```
73 */
74 @ExperimentalKairosApi
<lambda>null75 fun <A> Lazy<Transactional<A>>.defer(): Transactional<A> = deferInline { value }
76
77 /**
78 * Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
79 * [block].
80 *
81 * When the returned [Transactional] is accessed by the Kairos network, [block] will be invoked and
82 * the returned [Transactional] will be used.
83 *
84 * Useful for recursive definitions.
85 */
86 @ExperimentalKairosApi
deferredTransactionalnull87 fun <A> deferredTransactional(block: KairosScope.() -> Transactional<A>): Transactional<A> =
88 deferInline {
89 NoScope.block()
90 }
91
deferInlinenull92 private inline fun <A> deferInline(
93 crossinline block: InitScope.() -> Transactional<A>
94 ): Transactional<A> =
95 Transactional(StateInit(init(name = null) { block().impl.init.connect(evalScope = this) }))
96
97 /**
98 * Returns a [Transactional]. The passed [block] will be evaluated on demand at most once per
99 * transaction; any subsequent sampling within the same transaction will receive a cached value.
100 *
101 * @sample com.android.systemui.kairos.KairosSamples.sampleTransactional
102 */
103 @ExperimentalKairosApi
transactionallynull104 fun <A> transactionally(block: TransactionScope.() -> A): Transactional<A> =
105 Transactional(stateOf(transactionalImpl { block() }))
106
107 /** Returns a [Transactional] that, when queried, samples this [State]. */
asTransactionalnull108 fun <A> State<A>.asTransactional(): Transactional<A> =
109 Transactional(map { TransactionalImpl.Const(CompletableLazy(it)) })
110