Created
August 9, 2025 14:56
-
-
Save mahdiPourkazemi/4a26c25c6adf1a2dd70b1c78663d075e to your computer and use it in GitHub Desktop.
Function Composition simple and with flow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun PriceScreen() { | |
val originalPrice = 150.0 | |
val addTax = { price: Double -> price * 1.08 } | |
val applyDiscount = { price: Double -> price * 0.85 } | |
val roundPrice = { price: Double -> kotlin.math.round(price) } | |
val finalPrice = processPrice(originalPrice, applyDiscount, addTax, roundPrice) | |
Text( | |
text = "Final Price: $finalPrice", | |
style = MaterialTheme.typography.headlineMedium | |
) | |
} | |
fun processPrice( | |
originalPrice: Double, | |
vararg operations: (Double) -> Double | |
): String { | |
var result = originalPrice | |
operations.forEach { operation -> | |
result = operation(result) | |
} | |
return "$${"%.2f".format(result)}" | |
} | |
//################################################################# | |
// --- عملیاتها --- | |
val addTax: (Double) -> Double = { price -> price * 1.08 } | |
val applyDiscount: (Double) -> Double = { price -> price * 0.85 } | |
val roundPrice: (Double) -> Double = { price -> kotlin.math.round(price) } | |
// فرمت کردن قیمت | |
fun Double.formatCurrency(): String = | |
"$${"%.2f".format(this)}" | |
// --- Extension Function برای pipe --- | |
fun Double.pipe(operation: (Double) -> Double): Double = | |
operation(this) | |
val originalPrice = 150.0 | |
// نسخه pipe chain | |
val finalPricePipe = originalPrice | |
.pipe(applyDiscount) | |
.pipe(addTax) | |
.pipe(roundPrice) | |
.formatCurrency() | |
//################################################################# | |
// فرمت کننده | |
fun Double.formatCurrency(): String = | |
"$${"%.2f".format(this)}" | |
// توابع ساده (غیر-suspending) | |
val applyDiscount: (Double) -> Double = { price -> price * 0.85 } // 15% off | |
val addTax: (Double) -> Double = { price -> price * 1.08 } // +8% tax | |
val roundPrice: (Double) -> Double = { price -> kotlin.math.round(price) } | |
// نمونهٔ عملیاتِ suspend (مثال: گرفتن نرخ تخفیف داینامیک از شبکه) | |
val applyDiscountFromNetwork: suspend (Double) -> Double = { price -> | |
// فرض: call network -> get dynamic discount | |
// برای مثال ساده، فقط همان 15%: | |
price * 0.85 | |
} | |
/** | |
* Compose a sequence of suspend operations and apply them sequentially to each emitted value. | |
* Because Flow's map lambda is suspend, we can call suspend ops inside it. | |
*/ | |
fun <T> Flow<T>.compose(vararg ops: suspend (T) -> T): Flow<T> = | |
map { value -> | |
var result = value | |
for (op in ops) { | |
result = op(result) | |
} | |
result | |
} | |
class PriceViewModel : ViewModel() { | |
private val _originalPrice = MutableStateFlow(150.0) // ورودی قابل تغییر | |
//val originalPrice: StateFlow<Double> = _originalPrice.asStateFlow() | |
// pipeline: اعمال تخفیف، اضافه کردن مالیات، رند کردن، سپس فرمت | |
val finalPriceFlow: StateFlow<String> = _originalPrice | |
.compose(applyDiscountFromNetwork, addTax, roundPrice) // compose از بالا | |
.map { it.formatCurrency() } // تبدیل به رشته | |
.flowOn(Dispatchers.Default) // محاسبات را در پسزمینه انجام بده | |
.stateIn(viewModelScope, SharingStarted.Eagerly, "$0.00") | |
fun setPrice(p: Double) { | |
_originalPrice.value = p | |
} | |
} | |
@Composable | |
fun PriceScreen(viewModel: PriceViewModel = viewModel()) { | |
val finalPrice by viewModel.finalPriceFlow.collectAsState() | |
Column(modifier = Modifier.padding(16.dp)) { | |
Text(text = "Final Price: $finalPrice", style = MaterialTheme.typography.headlineSmall) | |
// مثلاً دکمه برای تغییر قیمت | |
Button(onClick = { viewModel.setPrice(200.0) }) { | |
Text("Set price = 200") | |
} | |
} | |
} | |
//################################################################# | |
val addTax: (Double) -> Double = { it * 1.08 } | |
val roundPrice: (Double) -> Double = { kotlin.math.round(it) } | |
val discountFromApi: suspend (Double) -> Double = { price -> | |
// simulate network delay | |
delay(100) | |
price * 0.85 | |
} | |
val pricePipeline = suspendPipeline(discountFromApi, addTax, roundPrice) | |
viewModelScope.launch { | |
val result = pricePipeline(150.0) | |
println(result) // خروجی نهایی بعد از تخفیف، مالیات، رند کردن | |
} | |
//################################################################# | |
import kotlinx.coroutines.* | |
import kotlin.coroutines.CoroutineContext | |
// نوع دادهی مرحله (operation) که dispatcher مخصوص خودش رو هم میتونه داشته باشه | |
data class Stage<T>( | |
val dispatcher: CoroutineContext, | |
val op: suspend (T) -> T | |
) | |
// سازنده Pipeline | |
fun <T> suspendPipeline(vararg stages: Stage<T>): suspend (T) -> T = { input -> | |
var result = input | |
for (stage in stages) { | |
withContext(stage.dispatcher) { | |
result = stage.op(result) | |
} | |
} | |
result | |
} | |
// نسخه کمکی برای Stage ساختن راحتتر | |
fun <T> ioStage(op: suspend (T) -> T) = Stage(Dispatchers.IO, op) | |
fun <T> cpuStage(op: suspend (T) -> T) = Stage(Dispatchers.Default, op) | |
fun <T> mainStage(op: suspend (T) -> T) = Stage(Dispatchers.Main, op) | |
val discountFromApi: suspend (Double) -> Double = { price -> | |
delay(100) // simulate network | |
price * 0.85 | |
} | |
val addTax: (Double) -> Double = { it * 1.08 } | |
val formatCurrency: (Double) -> String = { "$${"%.2f".format(it)}" } | |
val pricePipeline = suspendPipeline( | |
ioStage(discountFromApi), | |
cpuStage { addTax(it) }, | |
mainStage { println("Price: ${formatCurrency(it)}"); it } | |
) | |
viewModelScope.launch { | |
pricePipeline(200.0) // اجرا به ترتیب و روی Dispatcher مناسب | |
} | |
//################################################################# | |
import kotlinx.coroutines.* | |
import kotlinx.coroutines.flow.* | |
import kotlin.coroutines.CoroutineContext | |
// دادهی مرحله که Dispatcher هم داره | |
data class Stage<T>( | |
val dispatcher: CoroutineContext, | |
val op: suspend (T) -> T | |
) | |
// کمکیها برای Stageسازی راحت | |
fun <T> ioStage(op: suspend (T) -> T) = Stage(Dispatchers.IO, op) | |
fun <T> cpuStage(op: suspend (T) -> T) = Stage(Dispatchers.Default, op) | |
fun <T> mainStage(op: suspend (T) -> T) = Stage(Dispatchers.Main, op) | |
// نسخه Flow-based | |
fun <T> Flow<T>.suspendPipelineFlow(vararg stages: Stage<T>): Flow<T> { | |
return this.map { initial -> | |
var result = initial | |
for (stage in stages) { | |
withContext(stage.dispatcher) { | |
result = stage.op(result) | |
} | |
} | |
result | |
} | |
} | |
val discountFromApi: suspend (Double) -> Double = { price -> | |
delay(200) // simulate API | |
price * 0.85 | |
} | |
val addTax: (Double) -> Double = { it * 1.08 } | |
val formatCurrency: (Double) -> String = { "$${"%.2f".format(it)}" } | |
class PriceViewModel : ViewModel() { | |
private val _originalPrice = MutableStateFlow(200.0) | |
val finalPriceFlow: StateFlow<String> = _originalPrice | |
.suspendPipelineFlow( | |
ioStage(discountFromApi), | |
cpuStage { addTax(it) }, | |
mainStage { println("UI logging: $it"); it } | |
) | |
.map { formatCurrency(it) } | |
.stateIn(viewModelScope, SharingStarted.Lazily, formatCurrency(200.0)) | |
fun updatePrice(newPrice: Double) { | |
_originalPrice.value = newPrice | |
} | |
} | |
@Composable | |
fun PriceScreen(viewModel: PriceViewModel = viewModel()) { | |
val finalPrice by viewModel.finalPriceFlow.collectAsState() | |
Column { | |
Text("Final Price: $finalPrice") | |
Button(onClick = { viewModel.updatePrice((100..500).random().toDouble()) }) { | |
Text("Random Price") | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment