jrgonzalez
08/01/2022, 8:50 AMhttps://www.youtube.com/watch?v=g79A6HmbW5M&t=1786s▾
dimsuz
08/01/2022, 3:27 PMval lens1: Lens<S, A>
val lens2: Lens<S, B>
how do I apply them one after another to change field A
then change field B
and return S
?
fun modify(s: S): S {
return lens1.modify { a.doStuff() } ?? lens2.modify { b.doStuff() }
}
i.e. I want not to compose them but to sequence them.stojan
08/03/2022, 1:28 PMTower Guidev2
08/03/2022, 2:25 PMLukasz Kalnik
08/03/2022, 3:20 PMeither
over either.eager
for suspending computations?
If I use either.eager
, will it simply block the current thread?
suspend fun doSomethingLong(): Either<Error, Unit>
either.eager {
doSomethingLong() // does it block the current thread?
}
Tower Guidev2
08/05/2022, 7:00 AMEither
while completing related operations
for example i am calling retrofit
api calls then persisting the data into my local sqlite
database
i am employing io.arrow-kt:arrow-core-retrofit
for my retrofit
calls as follows:-
/**
* <https://api.discogs.com/artists/45467>
*
*/
@GET("artists/{artist_id}")
suspend fun artist(@Header(HEADER_AUTHORISATION) accessToken: String = ACCESS_TOKEN, @Path("artist_id") artistId: Long): Either<CallError, ArtistResponse>
then in my worker:-
override suspend fun doActualWork(): Result {
return repository
.artist(artistId = 45467)
.fold(
ifLeft = { callError -> consumeCallError(callError) },
ifRight = {
val artist = ARTIST_MAPPER.map(it)
println(artist)
repository.database.artistDao().insertAsync(artist)
Result.success()
})
}
how can "wrap" or employ Either for the code i execute in my ifRight block above?
e.g. the database insert. As I am using Room and it does not accept Either typerednifre
08/07/2022, 9:35 AMoption
or either
suspend functions? Wouldn’t they work just as well without suspend?Yannick Lazzari
08/08/2022, 3:04 PMeffect
inspired builder/DSL that I created to wrap calls to gRPC services. Details inside the thread.janvladimirmostert
08/08/2022, 9:18 PMdoSomething<T1, Tn, ..., Tn>(...)
that would output On
In other words, if I provide 5 generics, I want an O5<T1, T2, T3, T4, T5>
as output and if I define 1 generic, I want O1<T1>
as outputdarkmoon_uk
08/09/2022, 2:34 AMType alias SomeAlias should have a descriptor
...where SomeAlias
is any Kotlin typealias that has a generic parameter (of several that we have in our project).
Is Arrow known to not support generic type-aliases?
Is there an annotation to suppress analysis for such types?Emil Kantis
08/10/2022, 10:02 PMEmployee
for instance.. I want it to have non-null values for a lot of fields which actually are nullable in the underlying system. So I would like to have my client deserialize the API response into a User
and then use a toEmployee
method that returns a ValidatedNel<Problem, Employee>
.. More in thread.Yannick Lazzari
08/12/2022, 12:25 AMValidated
vs. ValidatedNel
). The tutorial and most (or even all) examples I've seen on using the Validated
type declare all validation functions using ValidatedNel
type, even if the function only really ever returns a single error. For instance, a string field validation function that checks the length will either return a (single) length validation error, or the valid string, but the error is wrapped in the Nel
. And the only reason I can think of is because when zipping the validated types together, one can simply do
val someEntity: Validated<DomainError, SomeEntity> =
validateField1()
.zip(SemiGroup.nonEmptyList(),
validateField2(),
validateField3()) { (v1, v2, v3) -> SomeEntity(v1, v2, v3) }
i.e. not worry about dealing with Validated
vs. ValidatedNel
. I personally think the semantics are not accurate for these validation functions as we're false advertising that there could be multiple errors returned by each validation function. I think we should use the Nel
type when the function can truly return multiple errors, otherwise you should stick with Validated
. When zipping all your validated types together, it's really trivial to "sprinkle" a little toValidatedNel
here and there to then accumulate all the errors together:
val someEntity: Validated<DomainError, SomeEntity> =
validateField1SingleError().toValidatedNel()
.zip(SemiGroup.nonEmptyList(),
validateField2MultipleErrors(),
validateField3SingleError().toValidatedNel()) { (v1, v2, v3) -> SomeEntity(v1, v2, v3) }
Really nitpicking here, but I'm just curious to get other people's thoughts. Thanks!Yannick Lazzari
08/12/2022, 12:55 AMarrow.core.computations.either
object as opposed to its replacement arrow.core.continuations.either
.Yannick Lazzari
08/12/2022, 12:57 AMValidated
datatype? Or is it not possible? I think it would make composing validation functions easier.Yannick Lazzari
08/12/2022, 12:57 AMLukáš Kúšik
08/12/2022, 9:46 AM(1) Building applications with Kotlin and Arrow.kt in style - YouTube▾
Repository
which uses a RemoteApi
and I'd like to create a new user on the backend.
The RemoteApi
does the POST call using Ktor, and returns the result of the Either<ApiError, UserDTO>
type, where ApiError
encapsules any network exceptions, internal server errors, etc.
Now, I would like to parse expected errors (like UserAlreadyExists
) as UserError
of the common DomainError
sealed type (similar to the video).
I'd like the Repository
to get the`Either<ApiError, UserDTO>` from the RemoteApi
, process the known errors and return something like the Either<UserError, UserDTO>
type.
The problem with Either<UserError, UserDTO>
is, thatLukáš Kúšik
08/12/2022, 10:17 AM(1) Building applications with Kotlin and Arrow.kt in style - YouTube▾
Repository
which uses a RemoteApi
and I'd like to create a new user on the backend.
The RemoteApi
does the POST call using Ktor, and returns the result of the Either<ApiError, UserDTO>
type, where ApiError
encapsules any network exceptions, internal server errors, etc.
Now, I would like to parse expected errors (like UserAlreadyExists
) as UserError
of the common DomainError
sealed type (similar to the video).
I'd like the Repository
to get the`Either<ApiError, UserDTO>` from the RemoteApi
, process the known errors and return something like the Either<UserError, UserDTO>
type.
The problem with Either<UserError, UserDTO>
is, that when a network exception occurs, it is an ApiError
and not a UserError
. The Repository
would have to return something like Either<Either<ApiError,UserError>, UserDTO>
which looks weird, or?
So with a hierarchy like this:
sealed interface Error
Lukáš Kúšik
08/12/2022, 10:31 AM(1) Building applications with Kotlin and Arrow.kt in style - YouTube▾
sealed interface Error
sealed interface ApiError : Error {
object NetworkError : ApiError
object Unauthorized : ApiError
object ServerError : ApiError
}
sealed interface DomainError : Error
sealed interface UserError : DomainError {
object AlreadyExists : UserError
}
sealed interface BookError : DomainError {
object AlreadyReserved : UserError
}
class RemoteApi {
fun createUser(): Either<ApiError, UserDTO>
}
class Repository {
fun createUser(): Either<UserError, UserDTO> (?) {
val response: Either<ApiError, UserDTO> = remoteApi.createUser()
// ... map known errors to UserError and return
}
}
Let's say that there's a Repository
which uses a RemoteApi
and I'd like to create a new user on the backend.
The RemoteApi
does the POST call using Ktor, and returns the result of the Either<ApiError, UserDTO>
type, where ApiError
encapsules any network exceptions, internal server errors, etc.
Now, I would like to parse expected errors (like UserAlreadyExists
) as UserError
of the common DomainError
sealed type (similar to the video).
I'd like the Repository
to get the`Either<ApiError, UserDTO>` from the RemoteApi
, process the known errors and return something like the Either<UserError, UserDTO>
type.
The problem with Either<UserError, UserDTO>
is, that when a network exception occurs, it is an ApiError
and not a UserError
. The Repository
would have to return something like Either<Either<ApiError, UserError>, UserDTO>
which looks weird, or?
You could return Either<Error, UserDTO>
to contain both ApiError
and UserError
, but then you would also allow the Error
to be a BookError
, which would get ugly in when statements.
Maybe using Either<Error<UserError>, UserDTO>
would be the best, but I'm having trouble modelling the data types like that.
Does anyone know of an elegant solutions for this, or this problem of modelling errors in repositories overall? Thank you.tavish pegram
08/16/2022, 8:35 PMsealed interface FooFailure {
object Failure1: FooFailure
object Failure2: FooFailure
}
fun foo(): Either<FooFailure, Unit> = TODO()
sealed interface BarFailure {
data class FooFailed(message: String): BarFailure
object Failure1: BarFailure
...
}
fun bar(input: Input): Either<BarFailure, Unit> = either {
input.validate().bind() // Could be BarFailure.Failure1
foo().mapLeft { when (it) {
FooFailure.Failure1 -> BarFailure.FooFailed("Failure 1")
FooFailure.Failure2 -> BarFailure.FooFailed("Failure 2")
}}.bind()
}
maybe
sealed interface FooFailure {
object Failure1: FooFailure
object Failure2: FooFailure
}
fun foo(): Either<FooFailure, Unit> = TODO()
sealed interface BarFailure {
object Failure1: BarFailure
...
}
fun bar(input: Input): Either<BarFailure|FooFailure, Unit> = either {
input.validate().bind() // Could be BarFailure.Failure1 or some other bar specific failure
foo().bind()
}
but still being able to exhaustively when/pattern match on it. Ideally, in a flat way, but even having it be nested is fine.
bar(someInput()).fold(
ifLeft = { when (it) {
FooFailure.Failure1 -> TODO()
FooFailure.Failure2 -> TODO()
BarFailure.Failure1 -> TODO()
}},
ifRight = { TODO() }
)
Thanks!Emil Kantis
08/17/2022, 8:32 PMString? -> Validated<E, String> -> Validated<E, A>
(in my case converting a String? to UInt).
First step is easy with fromNullable
, but I'm not sure how to map a validated while catching.. I think I'm looking for a mapCatching(handleError: (Throwable) -> E, f: (A) -> B)
but it doesn't seem to exist? 🙂Dirk
08/18/2022, 3:36 PMStylianos Gakis
08/19/2022, 2:27 PMensureNotNull
exists as an extension function on EffectScope
Hm I tried to use the ensure function public suspend fun ensure(condition: Boolean, shift: () -> R)
inside the EffectScope
scope today and I was missing the nice functionality of the kotlin require
function which by using a contract allows the compiler to auto-infer the type after that function. Specifically when doing a require not null check in my case.
Since the ensure function does not do that, is there some other API I’m missing which could help me out, or should I just do !!
since I know myself at that point that the item is not null?rcd27
08/19/2022, 6:21 PMMasrur Mirboboev
08/22/2022, 6:51 AMarrow-optics
into one of our projects, but stumbled upon an issue. When declaring a typealias
in the same file where I have @optics
annotation, build of the gradle application fails with Duplicated JVM name error
. I was able to overcome the issue by declaring @file:JvmName("JvmNameOfFile")
, but quite curious to know if there are any other more elegant ways to fix it and fix the root cause. Any help would be much appreciated 🙏sam
08/22/2022, 8:50 AMraulraja
08/22/2022, 12:17 PMEffectScope
and the operation shift
to something else. 99% of the time shift is used for error propagation or short-circuiting.
We were wondering which one of the following you prefer:
1️⃣ Shift
context(Shift<Error>)
suspend fun example(): Int = shift(Error)
2️⃣ Raise
context(Raise<Error>)
suspend fun example(): Int = raise(Error)
3️⃣ CanFail
context(CanFail<Error>)
suspend fun example(): Int = fail(Error)
For more context on this see https://github.com/arrow-kt/arrow/pull/2797#issuecomment-1222239640
If you are interested in helping us decide please vote with one of those numbers or feel free to propose a new one or any question as thread comments to this post. Thanks!thanh
08/22/2022, 1:40 PMSam
08/22/2022, 3:25 PMshift
return Nothing
?
It’s declared as
suspend fun <B> shift(r: R): B
but it could instead be
suspend fun shift(r: R): Nothing
The reason I ask is specifically because of this use case:
var failure: MyFailure? = null
val effect = eagerEffect<MyFailure, String> {
failure?.let { shift(it) } // "Not enough information to infer type variable B"
"some result"
}
The fix is to be explicit about the Nothing
type, which feels super weird:
val effect = eagerEffect<MyFailure, String> {
failure?.let { shift<Nothing>(it) }
"some result"
}
Lukasz Kalnik
08/23/2022, 8:51 AMval
property in the init
block of a class based on some Either
value. However, whenever I use any of the Either
related functions like fold
or either.eager
block (which take a lambda as parameter) and I try to set the class property inside the lambda I get the error "Captured member variables initialization is forbidden due to possible reassignment".
Is my only option really something like:
myProperty = if (either.isRight()) either.value else defaultValue
?Jörg Winter
08/23/2022, 11:19 AMfun createUser(bookInput: BookInput): ResponseEntity<ParsedBookResponse> =
with(bookInput) {
withParsedBook().fold(....)
...we can have this ?
fun createUser(bookInput: BookInput): ResponseEntity<ParsedBookResponse> =
withParsedBook().fold(....)
...by annotation + compiler plugin or Kotlin context receiver ?
Currently I have this function:
context(BookInput)
fun withParsedBook(): Validated<NonEmptyList<String>, ParsedBook> {....}
from this example I guess we still need that context<MyDependency> instead of with(MyDependency) ?Jörg Winter
08/23/2022, 11:19 AMfun createUser(bookInput: BookInput): ResponseEntity<ParsedBookResponse> =
with(bookInput) {
withParsedBook().fold(....)
...we can have this ?
fun createUser(bookInput: BookInput): ResponseEntity<ParsedBookResponse> =
withParsedBook().fold(....)
...by annotation + compiler plugin or Kotlin context receiver ?
Currently I have this function:
context(BookInput)
fun withParsedBook(): Validated<NonEmptyList<String>, ParsedBook> {....}
from this example I guess we still need that context<MyDependency> instead of with(MyDependency) ?raulraja
08/23/2022, 12:56 PMwith
and a lambda you will be able to bring @Provider
into the scope using context<A, B, C, ...>
. for example:Jörg Winter
08/23/2022, 1:01 PMraulraja
08/23/2022, 1:13 PMcontext<A, B, ...>
. If you had declared instead those as context parameters to the function then when you call the function you have to summon them with context
. It's possible to make it all implicit and just allow the call to resolve them magically based on what is missing, but we have not made a decision yet on whether that is a good idea or context<A, B..
should be explicit.