KEMBAR78
Pure function And Functional Error Handling | PDF
순수함수와 함수형 에러 처리
김규하
왜?!
왜?!
왜?!
• RxJava2 지원 종료
왜?!
• RxJava2 지원 종료

• Coroutine 적용에 따른 RxJava 대체제가 필요
Custom WrapperFlow VS
왜?!
Custom Wrapper
왜?!
순수 함수?
순수 함수?
fun pureFunction(value: Any): Result {
// doSomething
return ...
}
순수 함수?
순수 함수?
순수 함수?
사이드이펙트 X
순수 함수?
사이드이펙트 X

참조투명성 O
순수하지 않은 함수?
순수하지 않은 함수?
val otherValues: MutableMap<String, Any> = mutableMapOf()
fun imPureFunction(): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
순수하지 않은 함수?
val otherValues: MutableMap<String, Any> = mutableMapOf()
fun imPureFunction(): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
fun imPureFunction(otherValue: MutableMap<String, Any>): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
순수하지 않은 함수?
val otherValues: MutableMap<String, Any> = mutableMapOf()
fun imPureFunction(): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
fun imPureFunction(otherValue: MutableMap<String, Any>): Result {
val value = otherValue[MAP_KEY] ?: throw IllegalArgumentException("Error")
// doSomething
return ...
}
fun imPureFunction(otherValue: MutableMap<String, Any>): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
잠깐!?
순수하지 않은 함수?
val otherValues: MutableMap<String, Any> = mutableMapOf()
fun imPureFunction(): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
fun imPureFunction(otherValue: MutableMap<String, Any>): Result {
val value = otherValue[MAP_KEY] ?: throw IllegalArgumentException("Error")
// doSomething
return ...
}
fun imPureFunction(otherValue: MutableMap<String, Any>): Result {
val value = otherValue[MAP_KEY]
// doSomething
return ...
}
순수하지 않은 함수?
fun imPureFunction(otherValue: MutableMap<String, Any>): Result {
val value = otherValue[MAP_KEY] ?: throw IllegalArgumentException("Error")
// doSomething
return ...
}
예외를 던지면 안된다구요?
예외
예외
java.lang.ArrayIndexOutOfBoundsException
예외
java.lang.ArrayIndexOutOfBoundsException
android.database.CursorWindowAllocationException
예외
java.lang.ArrayIndexOutOfBoundsException
java.lang.StringIndexOutOfBoundsException
android.database.CursorWindowAllocationException
예외
java.lang.ArrayIndexOutOfBoundsException
javax.net.ssl.SSLHandshakeException
java.lang.StringIndexOutOfBoundsException
java.lang.NoClassDefFoundError
android.database.CursorWindowAllocationException
예외
java.lang.IllegalArgumentException
java.lang.NullPointerException
java.lang.UnsatisfiedLinkError
java.lang.NoClassDefFoundError
java.lang.OutOfMemoryError
android.database.CursorWindowAllocationException
java.lang.StringIndexOutOfBoundsException
java.net.SocketTimeoutException
javax.net.ssl.SSLHandshakeException
java.lang.ArrayIndexOutOfBoundsException
세상에 예외가 이렇게 많은데?
어떻게?
예외
예외
예외가 왜 참조투명성을 해치는지?
예외
fun div(x: Int, y: Int): Int {
return x / y
}
println(div(1, 0))
예외
Exception in thread "main" java.lang.ArithmeticException: /
by zero…
예외
fun div(x: Int, y: Int): Int {
return x / y
}
println(div(1, 0))
예외
fun div(x: Int, y: Int): Int {
return try {
x / y
} catch (e: Exception) {
e.printStackTrace()
0
}
}
println(div(1, 0))
예외
Exception in thread "main" java.lang.ArithmeticException: /
by zero…

0
예외
fun div(x: Int, y: Int): Int {
return try {
x / y
} catch (e: Exception) {
e.printStackTrace()
0
}
}
println(div(1, 0))
예외
1. 예외는 참조 투명성을 해치고 처리를 위한 의존성을 도입한다.
예외
1. 예외는 참조 투명성을 해치고 처리를 위한 의존성을 도입한다.

2. 타입에 안전하지 않다.
예외
함수형에서는 어떻게?
함수형 에러 핸들링
함수형 에러 핸들링
• 예외도 보통의 값으로 표현
함수형 에러 핸들링
• 예외도 보통의 값으로 표현

• 예외의 관한 처리를 사용지점 까지 늦춘다.
함수형 에러 핸들링
sealed class FunResult<out T> {
data class Success<out T>(val data: T): FunResult<T>()
data class Failure(val error: Throwable): FunResult<Nothing>()
}
함수형 에러 핸들링
fun div(x: Int, y: Int): FunResult<Int> {
return try {
FunResult.Success(x / y)
} catch (e: Exception) {
FunResult.Error(e)
}
}
println(div(1, 0))
println(div(1, 1))
함수형 에러 핸들링
Error(error=java.lang.ArithmeticException: / by zero)
Success(data=1)
실제 서비스에서는 어떻게 

사용할까??
LocalDataSource
DataBase
View
ViewModel
Repository
RemoteDataSource
WebService
ViewModel
Repository
RemoteDataSource
WebService
ViewModel
Repository
RemoteDataSource
WebService
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getRemoteApi()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
ViewModel
ViewModel
Repository
RemoteDataSource
WebService
class SampleRepository(
private val remoteRemoteDataSource: SampleRemoteDataSource
) {
suspend fun getIOExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getRemoteApi(): List<SampleData> {
return remoteRemoteDataSource.getRemoteApi()
}
}
Repository
ViewModel
Repository
RemoteDataSource
WebService
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): List<SampleData> {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleData> {
return sampleService.getSampleData()
}
}
RemoteDataSource
ViewModel
Repository
RemoteDataSource
WebService
WebService
class SampleServiceImpl : SampleService {
override suspend fun getSampleData(): List<SampleData> =
coroutineScope {
delay(2000)
resultDataFromBackend
}
override suspend fun getTimeOutExceptionRemoteService(): List<SampleData> =
coroutineScope {
delay(5000)
throw TimeoutException("invalid data")
}
override suspend fun getIOExceptionRemoteService(): List<SampleData> =
coroutineScope {
delay(5000)
throw IOException("invalid data")
}
}
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): List<SampleData> {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleData> {
return sampleService.getSampleData()
}
}
RemoteDataSource
class SampleRepository(
private val remoteRemoteDataSource: SampleRemoteDataSource
) {
suspend fun getIOExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getRemoteApi(): List<SampleData> {
return remoteRemoteDataSource.getRemoteApi()
}
}
Repository
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getRemoteApi()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
ViewModel
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): List<SampleData> {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleData> {
return sampleService.getSampleData()
}
}
RemoteDataSource
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): List<SampleData> {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): List<SampleData> {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): List<SampleData> {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
Error!
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): Any {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
suspend fun getIOExceptionApi(): List<SampleData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): Any {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleDataAtData> {
return sampleService.getSampleData()
}
}
RemoteDataSource
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleDataAtData> {
return sampleService.getSampleData()
}
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleDataAtData> {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleDataAtData> {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): List<SampleDataAtData> {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
}
Error!
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): Any {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
}
class SampleRepository(
private val remoteRemoteDataSource: SampleRemoteDataSource
) {
suspend fun getIOExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): List<SampleData> {
return remoteRemoteDataSource.getSampleData()
}
}
Repository
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): List<SampleData> {
return remoteRemoteDataSource.getSampleData()
}
}
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): List<SampleData> {
return remoteRemoteDataSource.getSampleData()
}
}
Is Any type
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): List<SampleData> {
return remoteRemoteDataSource.getSampleData() as List<SampleData>
}
}
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): Any {
return remoteRemoteDataSource.getSampleData()
}
}
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
ViewModel
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
lass MainViewModel(
private val sampleRepository: SampleRepository
: ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
Is Any type List<SampleData>Or
lass MainViewModel(
private val sampleRepository: SampleRepository
: ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
Is Any type
lass MainViewModel(
private val sampleRepository: SampleRepository
: ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() } // Compile Error
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
lass MainViewModel(
private val sampleRepository: SampleRepository
: ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
List<SampleData>
lass MainViewModel(
private val sampleRepository: SampleRepository
: ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() } // do it or throw
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
sealed class FunResult<out T> {
data class Success<out T>(val data: T): FunResult<T>()
data class Failure(val error: Throwable): FunResult<Nothing>()
}
sealed class FunResult<out T> {
data class Success<out T>(val data: T) : FunResult<T>()
data class Error(val error: Throwable) : FunResult<Nothing>()
companion object {
suspend fun <T> safeCall(f: suspend () -> T): FunResult<T> = try {
Success(f.invoke())
} catch (e: Exception) {
Error(e)
}
}
}
suspend fun <T, R> FunResult<T>.map(f: suspend (T) -> R): FunResult<R> = when (this) {
is FunResult.Success<T> -> FunResult.Success(f.invoke(data))
is FunResult.Error -> this
}
suspend fun <T, R> FunResult<T>.flatMap(f: suspend (T) -> FunResult<R>): FunResult<R> = when (this) {
is FunResult.Success -> f.invoke(data)
is FunResult.Error -> this
}
suspend fun <T> FunResult<T>.fold(success: suspend (T) -> Unit, failure: (Throwable) -> Unit) = when (this) {
is FunResult.Success -> success.invoke(data)
is FunResult.Error -> failure.invoke(error)
}
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): Any {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): Any {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
}
RemoteDataSource
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): Any {
return try {
sampleService.getTimeOutExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> {
return FunResult.safeCall {
sampleService.getTimeOutExceptionRemoteService()
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
class SampleRemoteDataSource(
private val sampleService:
SampleService
) {
suspend fun
getTimeoutExceptionApi(): Any
{
return try {
sampleService.getTimeOutExcept
ionRemoteService()
} catch (e: Exception)
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> {
return FunResult.safeCall {
sampleService.getTimeOutExceptionRemoteService()
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
class SampleRemoteDataSource(
private val sampleService:
SampleService
) {
suspend fun
getTimeoutExceptionApi(): Any
{
return try {
sampleService.getTimeOutExcept
ionRemoteService()
} catch (e: Exception)
suspend fun <T> safeCall(f: suspend () -> T): FunResult<T> = try {
Success(f.invoke())
} catch (e: Exception) {
Error(e)
}
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> {
return FunResult.safeCall {
sampleService.getTimeOutExceptionRemoteService()
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
class SampleRemoteDataSource(
private val sampleService:
SampleService
) {
suspend fun
getTimeoutExceptionApi(): Any
{
return try {
sampleService.getTimeOutExcept
ionRemoteService()
} catch (e: Exception)
class SampleRemoteDataSource(
private val sampleService: SampleService
) {
suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> {
return FunResult.safeCall {
sampleService.getTimeOutExceptionRemoteService()
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): Any {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
}
RemoteDataSource
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getSampleData(): Any {
return try {
sampleService.getSampleData()
} catch (e: Exception) {
e.printStackTrace()
e
}
}
}
suspend fun getIOExceptionApi(): List<SampleDataAtData> {
return try {
sampleService.getIOExceptionRemoteService()
} catch (e: Exception) {
e.printStackTrace()
emptyList()
}
}
suspend fun getRemoteApi(): FunResult<List<SampleDataAtData>> {
return FunResult.safeCall {
sampleService.getSampleData()
}
}
}
class SampleRemoteDataSource(
private val sampleService:
SampleService
) {
suspend fun
getTimeoutExceptionApi():
FunResult<List<SampleData>> {
return
FunResult.safeCall {
sampleService.getTimeOutExcept
ionRemoteService()
class SampleRepository(
private val remoteRemoteDataSource: SampleRemoteDataSource
) {
suspend fun getIOExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): List<SampleData> {
return remoteRemoteDataSource.getSampleData()
}
}
Repository
class SampleRepository(
private val remoteRemoteDataSource: SampleRemoteDataSource
) {
suspend fun getIOExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getTimeoutExceptionApi(): List<SampleData> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
return response
}
suspend fun getSampleData(): List<SampleData> {
return remoteRemoteDataSource.getSampleData()
}
}
Repository
class SampleRepository(
private val remoteRemoteDataSource: SampleRemoteDataSource
) {
private val sampleDataAtDomainCache: HashMap<String, List<SampleData>> = hashMapOf()
suspend fun getIOExceptionApi(): FunResult<List<SampleData>> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
if (response is FunResult.Success) {
// do memory cache
}
return response
}
suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> {
val response = remoteRemoteDataSource.getTimeoutExceptionApi()
if (response is FunResult.Success) {
// do memory cache
}
return response
}
suspend fun getSampleData(): FunResult<List<SampleData>> {
val response = remoteRemoteDataSource.getSampleData()
if (response is FunResult.Success) {
// do memory cache
}
return response
}
}
class SampleRepository(
private val
remoteRemoteDataSource:
SampleRemoteDataSource
) {
suspend fun
getIOExceptionApi():
List<SampleData> {
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
ViewModel
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.toMainItem() }
launch(Dispatchers.Main) {
_mainItemList.value = result
}
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
class MainViewModel(
private val sampleRepository: SampleRepository
) : ViewModel() {
private val _mainItemList = MutableLiveData<List<MainItem>>()
val mainItemList: LiveData<List<MainItem>> get() = _mainItemList
private val _error = MutableLiveData<Throwable>()
val error: LiveData<Throwable> get() = _error
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.map { it.toMainItem() } }
result.fold(
success = {
launch(Dispatchers.Main) {
_mainItemList.value = it
}
},
failure = {
withContext(Dispatchers.Main) {
_error.value = it
}
}
)
}
}
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
private val _error = MutableLiveData<Throwable>()
val error: LiveData<Throwable> get() = _error
fun getMainItemList() {
viewModelScope.launch(Dispatchers.IO) {
val result = sampleRepository.getSampleData()
.map { it.map { it.toMainItem() } }
result.fold(
success = {
launch(Dispatchers.Main) {
_mainItemList.value = it
}
},
failure = {
withContext(Dispatchers.Main) {
_error.value = it
}
}
)
}
}
override fun onCleared() {
결론
결론
• 순수함수 작성이 쉬워졌다.
결론
• 순수함수 작성이 쉬워졌다.

• 예외도 하나의 값으로 실제 값을 사용할 때 처리 하므로 다른 함수
에서 신경쓸 필요가 없다.
결론
• 순수함수 작성이 쉬워졌다.

• 예외도 하나의 값으로 실제 값을 사용할 때 처리 하므로 다른 함수
에서 신경쓸 필요가 없다.

• 같은 데이터를 가져오는 레포지토리를 레고 블록 처럼 재사용 할
수 있다.
FunResult
. safeCall{ QnA }
감사합니다.

Pure function And Functional Error Handling

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
    왜?! • RxJava2 지원종료 • Coroutine 적용에 따른 RxJava 대체제가 필요
  • 6.
  • 7.
  • 8.
  • 9.
    순수 함수? fun pureFunction(value:Any): Result { // doSomething return ... }
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
    순수하지 않은 함수? valotherValues: MutableMap<String, Any> = mutableMapOf() fun imPureFunction(): Result { val value = otherValue[MAP_KEY] // doSomething return ... }
  • 16.
    순수하지 않은 함수? valotherValues: MutableMap<String, Any> = mutableMapOf() fun imPureFunction(): Result { val value = otherValue[MAP_KEY] // doSomething return ... } fun imPureFunction(otherValue: MutableMap<String, Any>): Result { val value = otherValue[MAP_KEY] // doSomething return ... }
  • 17.
    순수하지 않은 함수? valotherValues: MutableMap<String, Any> = mutableMapOf() fun imPureFunction(): Result { val value = otherValue[MAP_KEY] // doSomething return ... } fun imPureFunction(otherValue: MutableMap<String, Any>): Result { val value = otherValue[MAP_KEY] ?: throw IllegalArgumentException("Error") // doSomething return ... } fun imPureFunction(otherValue: MutableMap<String, Any>): Result { val value = otherValue[MAP_KEY] // doSomething return ... }
  • 18.
  • 19.
    순수하지 않은 함수? valotherValues: MutableMap<String, Any> = mutableMapOf() fun imPureFunction(): Result { val value = otherValue[MAP_KEY] // doSomething return ... } fun imPureFunction(otherValue: MutableMap<String, Any>): Result { val value = otherValue[MAP_KEY] ?: throw IllegalArgumentException("Error") // doSomething return ... } fun imPureFunction(otherValue: MutableMap<String, Any>): Result { val value = otherValue[MAP_KEY] // doSomething return ... }
  • 20.
    순수하지 않은 함수? funimPureFunction(otherValue: MutableMap<String, Any>): Result { val value = otherValue[MAP_KEY] ?: throw IllegalArgumentException("Error") // doSomething return ... }
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 31.
  • 32.
  • 33.
    예외 fun div(x: Int,y: Int): Int { return x / y } println(div(1, 0))
  • 34.
    예외 Exception in thread"main" java.lang.ArithmeticException: / by zero…
  • 35.
    예외 fun div(x: Int,y: Int): Int { return x / y } println(div(1, 0))
  • 36.
    예외 fun div(x: Int,y: Int): Int { return try { x / y } catch (e: Exception) { e.printStackTrace() 0 } } println(div(1, 0))
  • 37.
    예외 Exception in thread"main" java.lang.ArithmeticException: / by zero… 0
  • 38.
    예외 fun div(x: Int,y: Int): Int { return try { x / y } catch (e: Exception) { e.printStackTrace() 0 } } println(div(1, 0))
  • 39.
  • 40.
    1. 예외는 참조투명성을 해치고 처리를 위한 의존성을 도입한다. 예외
  • 41.
    1. 예외는 참조투명성을 해치고 처리를 위한 의존성을 도입한다. 2. 타입에 안전하지 않다. 예외
  • 42.
  • 43.
  • 44.
    함수형 에러 핸들링 •예외도 보통의 값으로 표현
  • 45.
    함수형 에러 핸들링 •예외도 보통의 값으로 표현 • 예외의 관한 처리를 사용지점 까지 늦춘다.
  • 46.
    함수형 에러 핸들링 sealedclass FunResult<out T> { data class Success<out T>(val data: T): FunResult<T>() data class Failure(val error: Throwable): FunResult<Nothing>() }
  • 47.
    함수형 에러 핸들링 fundiv(x: Int, y: Int): FunResult<Int> { return try { FunResult.Success(x / y) } catch (e: Exception) { FunResult.Error(e) } } println(div(1, 0)) println(div(1, 1))
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
    class MainViewModel( private valsampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getRemoteApi() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() } } ViewModel
  • 54.
  • 55.
    class SampleRepository( private valremoteRemoteDataSource: SampleRemoteDataSource ) { suspend fun getIOExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getRemoteApi(): List<SampleData> { return remoteRemoteDataSource.getRemoteApi() } } Repository
  • 56.
  • 57.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): List<SampleData> { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleData> { return sampleService.getSampleData() } } RemoteDataSource
  • 58.
  • 59.
    WebService class SampleServiceImpl :SampleService { override suspend fun getSampleData(): List<SampleData> = coroutineScope { delay(2000) resultDataFromBackend } override suspend fun getTimeOutExceptionRemoteService(): List<SampleData> = coroutineScope { delay(5000) throw TimeoutException("invalid data") } override suspend fun getIOExceptionRemoteService(): List<SampleData> = coroutineScope { delay(5000) throw IOException("invalid data") } }
  • 60.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): List<SampleData> { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleData> { return sampleService.getSampleData() } } RemoteDataSource
  • 61.
    class SampleRepository( private valremoteRemoteDataSource: SampleRemoteDataSource ) { suspend fun getIOExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getRemoteApi(): List<SampleData> { return remoteRemoteDataSource.getRemoteApi() } } Repository
  • 62.
    class MainViewModel( private valsampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getRemoteApi() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() } } ViewModel
  • 64.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): List<SampleData> { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleData> { return sampleService.getSampleData() } } RemoteDataSource
  • 65.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): List<SampleData> { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() }
  • 66.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): List<SampleData> { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() e } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() }
  • 67.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): List<SampleData> { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() e } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } Error!
  • 68.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() e } } suspend fun getIOExceptionApi(): List<SampleData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() }
  • 69.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() e } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleDataAtData> { return sampleService.getSampleData() } } RemoteDataSource
  • 70.
    suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleDataAtData> { return sampleService.getSampleData() } }
  • 71.
    } suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleDataAtData> { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() emptyList() } } }
  • 72.
    } suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleDataAtData> { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() e } } }
  • 73.
    } suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): List<SampleDataAtData> { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() e } } } Error!
  • 74.
    } suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): Any { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() e } } }
  • 75.
    class SampleRepository( private valremoteRemoteDataSource: SampleRemoteDataSource ) { suspend fun getIOExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): List<SampleData> { return remoteRemoteDataSource.getSampleData() } } Repository
  • 76.
    val response =remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): List<SampleData> { return remoteRemoteDataSource.getSampleData() } }
  • 77.
    val response =remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): List<SampleData> { return remoteRemoteDataSource.getSampleData() } } Is Any type
  • 78.
    return response } suspend fungetTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): List<SampleData> { return remoteRemoteDataSource.getSampleData() as List<SampleData> } }
  • 79.
    val response =remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): Any { return remoteRemoteDataSource.getSampleData() } }
  • 80.
    class MainViewModel( private valsampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() } } ViewModel
  • 81.
    class MainViewModel( private valsampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() }
  • 82.
    lass MainViewModel( private valsampleRepository: SampleRepository : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() Is Any type List<SampleData>Or
  • 83.
    lass MainViewModel( private valsampleRepository: SampleRepository : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() Is Any type
  • 84.
    lass MainViewModel( private valsampleRepository: SampleRepository : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } // Compile Error launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel()
  • 85.
    lass MainViewModel( private valsampleRepository: SampleRepository : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() List<SampleData>
  • 86.
    lass MainViewModel( private valsampleRepository: SampleRepository : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } // do it or throw launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel()
  • 88.
    sealed class FunResult<outT> { data class Success<out T>(val data: T): FunResult<T>() data class Failure(val error: Throwable): FunResult<Nothing>() }
  • 89.
    sealed class FunResult<outT> { data class Success<out T>(val data: T) : FunResult<T>() data class Error(val error: Throwable) : FunResult<Nothing>() companion object { suspend fun <T> safeCall(f: suspend () -> T): FunResult<T> = try { Success(f.invoke()) } catch (e: Exception) { Error(e) } } } suspend fun <T, R> FunResult<T>.map(f: suspend (T) -> R): FunResult<R> = when (this) { is FunResult.Success<T> -> FunResult.Success(f.invoke(data)) is FunResult.Error -> this } suspend fun <T, R> FunResult<T>.flatMap(f: suspend (T) -> FunResult<R>): FunResult<R> = when (this) { is FunResult.Success -> f.invoke(data) is FunResult.Error -> this } suspend fun <T> FunResult<T>.fold(success: suspend (T) -> Unit, failure: (Throwable) -> Unit) = when (this) { is FunResult.Success -> success.invoke(data) is FunResult.Error -> failure.invoke(error) }
  • 90.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() e } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): Any { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() e } } } RemoteDataSource
  • 91.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() e } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) {
  • 92.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> { return FunResult.safeCall { sampleService.getTimeOutExceptionRemoteService() } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() class SampleRemoteDataSource( private val sampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExcept ionRemoteService() } catch (e: Exception)
  • 93.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> { return FunResult.safeCall { sampleService.getTimeOutExceptionRemoteService() } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() class SampleRemoteDataSource( private val sampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExcept ionRemoteService() } catch (e: Exception)
  • 94.
    suspend fun <T>safeCall(f: suspend () -> T): FunResult<T> = try { Success(f.invoke()) } catch (e: Exception) { Error(e) } class SampleRemoteDataSource( private val sampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> { return FunResult.safeCall { sampleService.getTimeOutExceptionRemoteService() } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() class SampleRemoteDataSource( private val sampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): Any { return try { sampleService.getTimeOutExcept ionRemoteService() } catch (e: Exception)
  • 95.
    class SampleRemoteDataSource( private valsampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> { return FunResult.safeCall { sampleService.getTimeOutExceptionRemoteService() } } suspend fun getIOExceptionApi(): List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): Any { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() e } } } RemoteDataSource
  • 96.
    suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getSampleData(): Any { return try { sampleService.getSampleData() } catch (e: Exception) { e.printStackTrace() e } } }
  • 97.
    suspend fun getIOExceptionApi():List<SampleDataAtData> { return try { sampleService.getIOExceptionRemoteService() } catch (e: Exception) { e.printStackTrace() emptyList() } } suspend fun getRemoteApi(): FunResult<List<SampleDataAtData>> { return FunResult.safeCall { sampleService.getSampleData() } } } class SampleRemoteDataSource( private val sampleService: SampleService ) { suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> { return FunResult.safeCall { sampleService.getTimeOutExcept ionRemoteService()
  • 98.
    class SampleRepository( private valremoteRemoteDataSource: SampleRemoteDataSource ) { suspend fun getIOExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): List<SampleData> { return remoteRemoteDataSource.getSampleData() } } Repository
  • 99.
    class SampleRepository( private valremoteRemoteDataSource: SampleRemoteDataSource ) { suspend fun getIOExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getTimeoutExceptionApi(): List<SampleData> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() return response } suspend fun getSampleData(): List<SampleData> { return remoteRemoteDataSource.getSampleData() } } Repository
  • 100.
    class SampleRepository( private valremoteRemoteDataSource: SampleRemoteDataSource ) { private val sampleDataAtDomainCache: HashMap<String, List<SampleData>> = hashMapOf() suspend fun getIOExceptionApi(): FunResult<List<SampleData>> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() if (response is FunResult.Success) { // do memory cache } return response } suspend fun getTimeoutExceptionApi(): FunResult<List<SampleData>> { val response = remoteRemoteDataSource.getTimeoutExceptionApi() if (response is FunResult.Success) { // do memory cache } return response } suspend fun getSampleData(): FunResult<List<SampleData>> { val response = remoteRemoteDataSource.getSampleData() if (response is FunResult.Success) { // do memory cache } return response } } class SampleRepository( private val remoteRemoteDataSource: SampleRemoteDataSource ) { suspend fun getIOExceptionApi(): List<SampleData> {
  • 101.
    class MainViewModel( private valsampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() } } ViewModel
  • 102.
    class MainViewModel( private valsampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.toMainItem() } launch(Dispatchers.Main) { _mainItemList.value = result } } } override fun onCleared() { super.onCleared() viewModelScope.cancel() } } class MainViewModel( private val sampleRepository: SampleRepository ) : ViewModel() { private val _mainItemList = MutableLiveData<List<MainItem>>() val mainItemList: LiveData<List<MainItem>> get() = _mainItemList private val _error = MutableLiveData<Throwable>() val error: LiveData<Throwable> get() = _error fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.map { it.toMainItem() } } result.fold( success = { launch(Dispatchers.Main) { _mainItemList.value = it } }, failure = { withContext(Dispatchers.Main) { _error.value = it } } ) } } override fun onCleared() { super.onCleared() viewModelScope.cancel() } }
  • 103.
    private val _error= MutableLiveData<Throwable>() val error: LiveData<Throwable> get() = _error fun getMainItemList() { viewModelScope.launch(Dispatchers.IO) { val result = sampleRepository.getSampleData() .map { it.map { it.toMainItem() } } result.fold( success = { launch(Dispatchers.Main) { _mainItemList.value = it } }, failure = { withContext(Dispatchers.Main) { _error.value = it } } ) } } override fun onCleared() {
  • 104.
  • 105.
  • 106.
    결론 • 순수함수 작성이쉬워졌다. • 예외도 하나의 값으로 실제 값을 사용할 때 처리 하므로 다른 함수 에서 신경쓸 필요가 없다.
  • 107.
    결론 • 순수함수 작성이쉬워졌다. • 예외도 하나의 값으로 실제 값을 사용할 때 처리 하므로 다른 함수 에서 신경쓸 필요가 없다. • 같은 데이터를 가져오는 레포지토리를 레고 블록 처럼 재사용 할 수 있다.
  • 108.
  • 109.