Skip to content

Commit 6e53c87

Browse files
Return rendering instead of Unit
1 parent eec92fa commit 6e53c87

File tree

29 files changed

+463
-272
lines changed

29 files changed

+463
-272
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.squareup.benchmarks.performance.complex.poetry
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.MutableState
5+
import androidx.compose.runtime.mutableStateOf
6+
import androidx.compose.runtime.remember
7+
import androidx.test.ext.junit.runners.AndroidJUnit4
8+
import app.cash.molecule.AndroidUiDispatcher
9+
import app.cash.molecule.launchMolecule
10+
import com.google.common.truth.Truth.assertThat
11+
import kotlinx.coroutines.CoroutineScope
12+
import kotlinx.coroutines.ExperimentalCoroutinesApi
13+
import org.junit.Test
14+
import org.junit.runner.RunWith
15+
16+
@RunWith(AndroidJUnit4::class)
17+
class ComposeInheritanceTest {
18+
19+
abstract class Parent<T> {
20+
@Composable
21+
open fun AComposable(
22+
hoistToggleState: @Composable (s: T) -> Unit
23+
): Unit = throw IllegalStateException("I don't want to be a parent.")
24+
}
25+
26+
class Child<T>(
27+
private val payload: T
28+
): Parent<T>() {
29+
@Composable
30+
override fun AComposable(
31+
hoistToggleState: @Composable (s: T) -> Unit
32+
) {
33+
println("Can you hear me now? $payload")
34+
hoistToggleState(payload)
35+
}
36+
}
37+
38+
@Composable
39+
fun <T> Emitter(someObject: Parent<T>): T? {
40+
val payload: MutableState<T?> = remember { mutableStateOf(null) }
41+
someObject.AComposable {
42+
payload.value = it
43+
}
44+
return payload.value
45+
}
46+
47+
@OptIn(ExperimentalCoroutinesApi::class)
48+
@Test fun testComposableOverloading() {
49+
val child: Parent<String> = Child<String>("a test")
50+
val testScope = CoroutineScope(AndroidUiDispatcher.Main)
51+
52+
val testFlow = testScope.launchMolecule {
53+
Emitter(child)
54+
}
55+
56+
assertThat(testFlow.value).isEqualTo("a test")
57+
}
58+
59+
}

benchmarks/performance-poetry/complex-poetry/src/main/java/com/squareup/benchmarks/performance/complex/poetry/MaybeLoadingGatekeeperWorkflow.kt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,30 +54,25 @@ class MaybeLoadingGatekeeperWorkflow<T : Any>(
5454
renderProps: Unit,
5555
renderState: IsLoading,
5656
context: RenderContext,
57-
hoistRendering: @Composable (rendering: MayBeLoadingScreen) -> Unit
58-
) {
57+
): MayBeLoadingScreen {
5958
context.runningWorker(isLoading.asTraceableWorker("GatekeeperLoading")) {
6059
action {
6160
state = it
6261
}
6362
}
64-
context.ChildRendering(
63+
val maybeLoadingChild = context.ChildRendering(
6564
childWithLoading, childProps, "",
66-
hoistRendering = {
67-
hoistRendering(
68-
MayBeLoadingScreen(
69-
baseScreen = it,
70-
loaders = if (renderState) listOf(LoaderSpinner) else emptyList()
71-
)
72-
)
73-
}
7465
) {
7566
action(ActionHandlingTracingInterceptor.keyForTrace("GatekeeperChildFinished")) {
7667
setOutput(
7768
Unit
7869
)
7970
}
8071
}
72+
return MayBeLoadingScreen(
73+
baseScreen = maybeLoadingChild,
74+
loaders = if (renderState) listOf(LoaderSpinner) else emptyList()
75+
)
8176
}
8277

8378
override fun snapshotState(state: IsLoading): Snapshot? = null

benchmarks/performance-poetry/complex-poetry/src/main/java/com/squareup/benchmarks/performance/complex/poetry/PerformancePoemWorkflow.kt

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,7 @@ class PerformancePoemWorkflow(
245245
renderProps: Poem,
246246
renderState: State,
247247
context: RenderContext,
248-
hoistRendering: @Composable (rendering: OverviewDetailScreen) -> Unit
249-
) {
248+
): OverviewDetailScreen {
250249
when (renderState) {
251250
Initializing -> {
252251
// Again, the entire `Initializing` state is a smell, which is most obvious from the
@@ -264,7 +263,7 @@ class PerformancePoemWorkflow(
264263
state = Selected(NO_SELECTED_STANZA)
265264
}
266265
}
267-
hoistRendering(OverviewDetailScreen(overviewRendering = BackStackScreen(BlankScreen)))
266+
return OverviewDetailScreen(overviewRendering = BackStackScreen(BlankScreen))
268267
}
269268
else -> {
270269
val (stanzaIndex, currentStateIsLoading, repeat) = when (renderState) {
@@ -314,16 +313,21 @@ class PerformancePoemWorkflow(
314313
}
315314
}
316315

317-
val previousStanzas: MutableState<List<StanzaScreen>> = remember {
318-
mutableStateOf(emptyList())
319-
}
320-
val visibleStanza: MutableState<StanzaScreen?> = remember {
321-
mutableStateOf(null)
316+
val stanzaListOverview = context.ChildRendering(
317+
StanzaListWorkflow,
318+
StanzaListWorkflow.Props(
319+
poem = renderProps,
320+
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
321+
),
322+
key = "",
323+
) { selected ->
324+
HandleStanzaListOutput(simulatedPerfConfig, selected)
322325
}
326+
.copy(selection = stanzaIndex)
323327

324328
if (stanzaIndex != NO_SELECTED_STANZA) {
325-
renderProps.stanzas.subList(0, stanzaIndex)
326-
.forEachIndexed { index, _ ->
329+
val previousStanzas = renderProps.stanzas.subList(0, stanzaIndex)
330+
.mapIndexed { index, _ ->
327331
context.ChildRendering(
328332
StanzaWorkflow,
329333
Props(
@@ -332,67 +336,42 @@ class PerformancePoemWorkflow(
332336
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
333337
),
334338
key = "$index",
335-
hoistRendering = @Composable {
336-
previousStanzas.value = previousStanzas.value + it
337-
}
338339
) {
339340
noAction()
340341
}
341342
}
342-
context.ChildRendering(
343+
val visibleStanza = context.ChildRendering(
343344
StanzaWorkflow,
344345
Props(
345346
poem = renderProps,
346347
index = stanzaIndex,
347348
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
348349
),
349350
key = "$stanzaIndex",
350-
hoistRendering = @Composable {
351-
visibleStanza.value = it
352-
}
353351
) {
354352
when (it) {
355353
CloseStanzas -> ClearSelection(simulatedPerfConfig)
356354
ShowPreviousStanza -> SelectPrevious(simulatedPerfConfig)
357355
ShowNextStanza -> SelectNext(simulatedPerfConfig)
358356
}
359357
}
360-
}
361358

362-
val stackedStanzas = visibleStanza.value?.let {
363-
(previousStanzas.value + it).toBackStackScreen<Screen>()
364-
}
365-
366-
val stanzaListOverview: MutableState<StanzaListScreen?> = remember {
367-
mutableStateOf(null)
368-
}
369-
context.ChildRendering(
370-
StanzaListWorkflow,
371-
StanzaListWorkflow.Props(
372-
poem = renderProps,
373-
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
374-
),
375-
key = "",
376-
hoistRendering = @Composable {
377-
stanzaListOverview.value = it.copy(selection = stanzaIndex)
359+
val stackedStanzas = visibleStanza.let {
360+
(previousStanzas + it).toBackStackScreen<Screen>()
378361
}
379-
) { selected ->
380-
HandleStanzaListOutput(simulatedPerfConfig, selected)
381-
}
382362

383-
hoistRendering(
384-
stackedStanzas
385-
?.let {
386-
OverviewDetailScreen(
387-
overviewRendering = BackStackScreen(stanzaListOverview.value!!),
388-
detailRendering = it
389-
)
390-
} ?: OverviewDetailScreen(
391-
overviewRendering = BackStackScreen(stanzaListOverview.value!!),
392-
selectDefault = {
393-
context.actionSink.send(HandleStanzaListOutput(simulatedPerfConfig, 0))
394-
}
363+
return OverviewDetailScreen(
364+
overviewRendering = BackStackScreen(stanzaListOverview),
365+
detailRendering = stackedStanzas
395366
)
367+
}
368+
369+
370+
return OverviewDetailScreen(
371+
overviewRendering = BackStackScreen(stanzaListOverview),
372+
selectDefault = {
373+
context.actionSink.send(HandleStanzaListOutput(simulatedPerfConfig, 0))
374+
}
396375
)
397376
}
398377
}

benchmarks/performance-poetry/complex-poetry/src/main/java/com/squareup/benchmarks/performance/complex/poetry/PerformancePoemsBrowserWorkflow.kt

Lines changed: 70 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -160,99 +160,91 @@ class PerformancePoemsBrowserWorkflow(
160160
renderProps: List<Poem>,
161161
renderState: State,
162162
context: RenderContext,
163-
hoistRendering: @Composable (rendering: OverviewDetailScreen) -> Unit
164-
) {
163+
): OverviewDetailScreen {
165164
val poemListProps = Props(
166165
poems = renderProps,
167166
eventHandlerTag = ActionHandlingTracingInterceptor::keyForTrace
168167
)
169-
context.ChildRendering(
168+
169+
val poemListRendering = context.ChildRendering(
170170
child = PoemListWorkflow,
171171
props = poemListProps,
172172
key = "",
173-
hoistRendering = { poemListRendering ->
174-
when (renderState) {
175-
// Again, then entire `Initializing` state is a smell, which is most obvious from the
176-
// use of `Worker.from { Unit }`. A Worker doing no work and only shuttling the state
177-
// along is usually the sign you have an extraneous state that can be collapsed!
178-
// Don't try this at home.
179-
is Initializing -> {
180-
context.runningWorker(TraceableWorker.from("BrowserInitializing") { Unit }, "init") {
181-
isLoading.value = true
182-
action {
183-
isLoading.value = false
184-
state = NoSelection
185-
}
186-
}
187-
hoistRendering(OverviewDetailScreen(overviewRendering = BackStackScreen(BlankScreen)))
188-
}
189-
is NoSelection -> {
190-
hoistRendering(
191-
OverviewDetailScreen(
192-
overviewRendering = BackStackScreen(
193-
poemListRendering.copy(selection = NO_POEM_SELECTED)
194-
)
195-
)
173+
) { selected ->
174+
choosePoem(selected)
175+
}
176+
when (renderState) {
177+
// Again, then entire `Initializing` state is a smell, which is most obvious from the
178+
// use of `Worker.from { Unit }`. A Worker doing no work and only shuttling the state
179+
// along is usually the sign you have an extraneous state that can be collapsed!
180+
// Don't try this at home.
181+
is Initializing -> {
182+
context.runningWorker(TraceableWorker.from("BrowserInitializing") { Unit }, "init") {
183+
isLoading.value = true
184+
action {
185+
isLoading.value = false
186+
state = NoSelection
187+
}
188+
}
189+
return OverviewDetailScreen(overviewRendering = BackStackScreen(BlankScreen))
190+
}
191+
is NoSelection -> {
192+
return OverviewDetailScreen(
193+
overviewRendering = BackStackScreen(
194+
poemListRendering.copy(selection = NO_POEM_SELECTED)
196195
)
196+
)
197+
}
198+
is ComplexCall -> {
199+
context.runningWorker(
200+
TraceableWorker.from("ComplexCallBrowser(${renderState.payload})") {
201+
isLoading.value = true
202+
delay(simulatedPerfConfig.complexityDelay)
203+
// No Output for Worker is necessary because the selected index
204+
// is already in the state.
197205
}
198-
is ComplexCall -> {
199-
context.runningWorker(
200-
TraceableWorker.from("ComplexCallBrowser(${renderState.payload})") {
201-
isLoading.value = true
202-
delay(simulatedPerfConfig.complexityDelay)
203-
// No Output for Worker is necessary because the selected index
204-
// is already in the state.
205-
}
206-
) {
207-
action {
208-
isLoading.value = false
209-
(state as? ComplexCall)?.let { currentState ->
210-
state = if (currentState.payload != NO_POEM_SELECTED) {
211-
Selected(currentState.payload)
212-
} else {
213-
NoSelection
214-
}
215-
}
206+
) {
207+
action {
208+
isLoading.value = false
209+
(state as? ComplexCall)?.let { currentState ->
210+
state = if (currentState.payload != NO_POEM_SELECTED) {
211+
Selected(currentState.payload)
212+
} else {
213+
NoSelection
216214
}
217215
}
218-
val poemOverview = OverviewDetailScreen(
219-
overviewRendering = BackStackScreen(
220-
poemListRendering.copy(selection = renderState.payload)
221-
)
222-
)
223-
if (renderState.payload != NO_POEM_SELECTED) {
224-
context.ChildRendering(
225-
poemWorkflow,
226-
renderProps[renderState.payload],
227-
key = "",
228-
hoistRendering = { poem: OverviewDetailScreen ->
229-
hoistRendering(poemOverview + poem)
230-
}
231-
) { clearSelection }
232-
} else {
233-
hoistRendering(poemOverview)
234-
}
235-
}
236-
is Selected -> {
237-
val poemOverview = OverviewDetailScreen(
238-
overviewRendering = BackStackScreen(
239-
poemListRendering.copy(selection = renderState.poemIndex)
240-
)
241-
)
242-
context.ChildRendering(
243-
poemWorkflow,
244-
renderProps[renderState.poemIndex],
245-
key = "",
246-
hoistRendering = { poem: OverviewDetailScreen ->
247-
hoistRendering(poemOverview + poem)
248-
}
249-
) { clearSelection }
250216
}
251217
}
218+
val poemOverview = OverviewDetailScreen(
219+
overviewRendering = BackStackScreen(
220+
poemListRendering.copy(selection = renderState.payload)
221+
)
222+
)
223+
val poems = if (renderState.payload != NO_POEM_SELECTED) {
224+
poemOverview + context.ChildRendering(
225+
poemWorkflow,
226+
renderProps[renderState.payload],
227+
key = "",
228+
) { clearSelection }
229+
} else {
230+
poemOverview
231+
}
232+
return poems
233+
}
234+
is Selected -> {
235+
val poemOverview = OverviewDetailScreen(
236+
overviewRendering = BackStackScreen(
237+
poemListRendering.copy(selection = renderState.poemIndex)
238+
)
239+
)
240+
return poemOverview + context.ChildRendering(
241+
poemWorkflow,
242+
renderProps[renderState.poemIndex],
243+
key = "",
244+
) { clearSelection }
252245
}
253-
) { selected ->
254-
choosePoem(selected)
255246
}
247+
256248
}
257249

258250
override fun snapshotState(state: State): Snapshot? = null

0 commit comments

Comments
 (0)