Derek Berner
06/20/2019, 8:24 PMFx instance for EitherTstreetsofboston
06/20/2019, 8:25 PMIO<E, A> in a future version of arrowDerek Berner
06/20/2019, 8:25 PME has to be Throwable?Derek Berner
06/20/2019, 8:26 PMstreetsofboston
06/20/2019, 8:27 PMIO<E> is like a asynchronous `Try<E>`…. only supports Throwables for the ‘error’ type, if i’m not mistaken.
The IO<E, A> is not in arrow now, maybe in a future version… I remembe some discussions going on about this.streetsofboston
06/20/2019, 8:28 PMDerek Berner
06/20/2019, 8:29 PMIO that accepted an error type param, but when I realized that effectful comprehensions required the MonadThrow typeclass, and my MonadError instance didn't use throwables, I had to give up.Derek Berner
06/20/2019, 8:29 PMsuspend, and save IO for the edge of my program, and just use Either as my error monad, but I soon found out that Either comprehensions cannot have side effectsDerek Berner
06/20/2019, 8:30 PMsuspend functionDerek Berner
06/20/2019, 8:32 PMIO inside the Either, call sequence to convert Either<IO> into IO<Either>, use a mix of IO and Either comprehensions, then call unsafeRunAsync inside the suspend function to extract the value from the IO, but that's really messyDerek Berner
06/20/2019, 8:35 PMFx instance for EitherT but I'd run into the MonadThrow problem again.pakoito
06/20/2019, 8:35 PMpakoito
06/20/2019, 8:35 PMDerek Berner
06/20/2019, 8:36 PMraulraja
06/20/2019, 9:30 PMraulraja
06/20/2019, 9:31 PMDerek Berner
06/20/2019, 9:57 PMEither.catch got me part of the way, but EitherFx still doesn't permit !effect callsDerek Berner
06/20/2019, 9:57 PMEither.catch from inside an Either comprehensionDerek Berner
06/20/2019, 10:04 PMoverride suspend fun <T : Any> getFromCache(type: KClass<T>, key: String): Either<CacheGetError, T?> {
        val valueE = Either.catch(::CacheReadError andThen ::CacheGetError) {
            redis
                .get("$keyCache:$key")
                .await()
        }
        val serializerE = Json.context.getContextual(type).toOption()
            .toEither{ CacheGetError(NoSuchSerializer(type)) }
        return Either.fx<CacheGetError>().fx {
            val value = !valueE
            val serializer = !serializerE
            suspend { 
                Either.catch(::CacheDeserializationError andThen ::CacheGetError) {
                    (value?.let { withContext(<http://Dispatchers.IO|Dispatchers.IO>) { Json.nonstrict.parse(serializer, value) } })
                } 
            }
        }.fix().flatMap { it() }
    }Derek Berner
06/20/2019, 10:05 PMDerek Berner
06/20/2019, 10:09 PMoverride suspend fun <T : Any> getFromCache(type: KClass<T>, key: String): Either<CacheGetError, T?> {
        val valueE = Either.catch(::CacheReadError andThen ::CacheGetError) {
            redis
                .get("$keyCache:$key")
                .await()
        }
        val serializerE = Json.context.getContextual(type).toOption()
            .toEither{ CacheGetError(NoSuchSerializer(type)) }
        return Either.applicativeError<CacheGetError>()
            .tupled(valueE, serializerE)
            .flatMap { (value, serializer) ->
                Either.catch(::CacheDeserializationError andThen ::CacheGetError) {
                    (value?.let { withContext(<http://Dispatchers.IO|Dispatchers.IO>) { Json.nonstrict.parse(serializer, value) } })
                }
            }
    }raulraja
06/20/2019, 10:14 PMraulraja
06/20/2019, 10:15 PMDerek Berner
06/20/2019, 10:20 PMDerek Berner
06/20/2019, 10:22 PMfx will need additional work since !effect currently requires MonadThrowraulraja
06/20/2019, 10:23 PMraulraja
06/20/2019, 10:24 PMraulraja
06/20/2019, 10:24 PMDerek Berner
06/20/2019, 10:26 PMflatMap is inlined, I can work around it that way:  override suspend fun <T : Any> putInCache(
        type: KClass<T>,
        key: String,
        value: T?,
        seconds: Long
    ): Either<CachePutError, Unit> {
        val serializerE =
            Json.context.getContextual(type).toOption()
                .toEither{ CachePutError(NoSuchSerializer(type)) }
        val jsonStringE = serializerE.flatMap { serializer ->
            Either.catch(::CacheSerializationError andThen ::CachePutError) {
                value?.let { Json.plain.stringify(serializer, value) }
            }
        }
        val resultE = jsonStringE.flatMap { jsonString ->
            Either.catch(::CacheWriteError andThen ::CachePutError) {
                redis.set(
                    "$keyCache:$key",
                    jsonString,
                    SetArgs().nx().ex(seconds)
                ).await()
            }
        }
        return resultE
            .ensure({CachePutError(KeyExistsInCache(key))}) { result ->
                result == "OK"
            }
            .map(constant(Unit))
    }raulraja
06/20/2019, 10:30 PM