Ifvwm
04/18/2021, 12:01 PMDaniel Berg
04/20/2021, 3:00 AMIfvwm
04/20/2021, 8:21 AMeither { ... } ?
I can't find documents about it, related to
Either.fx<A,B> { ... } ?
drew
04/20/2021, 8:55 PMnull
, i want to return an error. if not, i want to run several validations against the non-null data. how would i go about this? .withEither
doesn’t seem correct, because i want to operate with Validated
, not Either
.rpillay
04/21/2021, 6:06 AMdrew
04/21/2021, 2:42 PMfun <E, A, B> Validated<E, A>.andThen(next: (A) -> Validated<E, B>): Validated<E, B> =
this.withEither { first ->
first.flatMap { next(it).toEither() }
}
Hexa
04/21/2021, 4:04 PMdependencies {
implementation("io.arrow-kt:arrow-core:0.13.1")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
Hexa
04/21/2021, 4:06 PMcarbaj0
04/27/2021, 11:50 AMlistEithers.map { it.toDomain() }.traverse(Either.applicative(), ::identity).map { it.fix().toList() }
vs
listEithers.map { it.toDomain() }.traverseEither { it }
Cody Mikol
04/27/2021, 5:47 PMlistOf(
{ foo() }
{ bar() }
{ baz() }
).parTraverseEither { it() }
Cody Mikol
04/28/2021, 12:21 AMrunBlocking
lambda have a negative impact on its behavior?
Given the scenario that I want a method capable of taking in a list of strings and then resolving something via HTTP I seem to have a two options.
1. Make the method suspended, this requires callers to be suspended as well
suspend fun parRequireStrings(resolvers: List<String>): ValidatedNel<Error, List<String>> = properties
.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
2 Make the method block so the callers don’t have to be suspended
fun parRequireStrings(resolvers: List<String>): ValidatedNel<Error, List<String>> = runBlocking {
properties.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
}
The second option seems more desirable as you can call it outside of suspended functions, are there any drawbacks to this approach?Cody Mikol
04/29/2021, 11:59 PMparTraverseEither
will short circuit and take the left path on the first error, is there some way to collect errors in parallel into a
Validated<Nel<L>,R>
Brad M
04/30/2021, 8:43 PMisEmpty
method that is functionally equivalent in our case to null? For example:
val files: Map<UUID, MultipartFile>;
val fileUrls = if (files?.isNotEmpty()) {
uploadFiles(files).bind() // returns Either<MyError, Map<UUID, Url>>
} else null
Satyam Agarwal
05/01/2021, 2:45 PMCoroutineScope.actor
. But was wondering if I can get something functional.Cody Mikol
05/06/2021, 7:12 PMRuntime exception Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: 'java.lang.Object arrow.core.Either$Right.getB()'
Jimmy Alvarez
05/11/2021, 2:47 AMdephinera
05/12/2021, 12:01 PMarrow.core.extensions.either.apply.mapN
function moved to? I expected it to be in Either.mapN
, however by using arrow-core 0.13.2, I can't find such reference. I couldn't find the release notes where that changed eitherJeff
05/12/2021, 6:58 PMsourceA().handleErrorWith {
sourceB().handleErrorWith {
sourceC()
}
}
Gopal S Akshintala
05/14/2021, 10:40 AMfilterOrElse
as Extensions to Either
instead of member functions? This won’t let to be used as a dot function when consumed from Java. I know Arrow is not designed for Java consumption, but is there any advantage of having this as extension over member? If not can these functions be made member fns, so we get an added advantage of consuming them from JavaCody Mikol
05/14/2021, 3:36 PMalso
in arrow for things like logging?
Currently doing something like
foo()
.mapLeft { it.also { logger.error("Something terrible has happened!") } }
.map { it.also { logger.success("Something good has happened!") } }
thanh
05/15/2021, 8:09 AMcarbaj0
05/15/2021, 11:44 AMBenoît
05/18/2021, 8:39 AMdnowak
05/18/2021, 10:52 AMIO {
//something may throw exception here
}.handleError { t ->
//map exception to some result
}
rpillay
05/20/2021, 4:09 AMsomeEither.flatMap { someCoroutineMethod() <- // this causes an error - "Suspension functions can be called only within coroutine body" }
If we were to make flatMap an inline function (ie.
inline fun <A, B, C> EitherOf<A, B>.flatMap(f: (B) -> Either<A, C>): Either<A, C> = ...
That would allow it to be called. This is similar to the .fold we have on Either. Is there a reason we shouldn't make this change?Peter
05/20/2021, 9:58 PMf.isDefinedAt
Jeff
05/21/2021, 6:00 PMmyEither.map {
try {
it.toC()
} catch (e: Exception) {
A()
}
}
Jeff
05/21/2021, 6:40 PMsuspend
to signal side-effecty functions. When I do this my IDE gives me helpful hints saying I don’t need the suspend
keyword since I don’t call any suspend functions. Is there a typical solution for this? Do I just suppress / annotate it away?EarthCitizen
05/25/2021, 6:37 PMarrow.core.extensions
package? I am trying to do Either.applicative()
, in case I am wrong about where to find thisTower Guidev2
05/27/2021, 5:13 PMTower Guidev2
05/27/2021, 5:13 PMJannis
05/27/2021, 5:34 PMIO = suspend
and Either
for modeling errors. To be clear, this is simply not equally powerful as higher kinded types in kotlin and there is a lot this cannot do, however it is easier to use and sufficient for most.
There are two common uses for higher kinds:
• monad stacks for effects: This is replaced by suspend
functions and comprehensions, however atm it is not equally powerful because kotlin continuations cannot be resumed multiple times and thus we can only model short-circuit-like monads with effects (such as either
or any nullable type). IO
is equal to suspend
in arrow terms.
• Abstracting over "containers" of values. This is simply not possible without kinds, suspend functions or not. If your use case falls under this category, you are better off rolling your own kind implementation 😕 But with arrow-meta this may very well come back at some point, but for now its simply not there...Either
. Kinds may or may not come back, but not in the form as before and certainly not without compiler pluginsraulraja
05/27/2021, 5:38 PMF
or transformer stacks for the case of IO which is what F is most of the time. It’s also much faster because the compiler optimizes suspend programs over your traditional IO based ones. Even with kinds, since in Kotlin they are emulated, partially applying type arguments of kinds like Either and downcasting with fix
is painful and something we don’t want to proliferate as idiom in Arrow.Tower Guidev2
05/28/2021, 8:28 AMInput
and Output
, what I require is to define that Input1
can only produce Output1
, and Input2
can only produce Output2
etc.. in addition I also need to define the data type contained in each Input
and Output
, so that Input1
can only contain a String
, and Output1
can only contain a Long
(I am only using these simple data types for example purposes, in reality I would use Complex custom data classes
) , wouldnt Higher Kinded Types solve this for me?, I am lost as to develop another solution that can both define a strict releationship between my Input & Output types and the data they contain.
I need to be able to define that Input1 can only ever contain DataTypeA and only ever produce Output1; That Output1 can only ever contain DataTypeB.
I wish to define an entire "family" of Input(s) and Output(s) that have the above restrictions.
Is that possible in Kotlin?streetsofboston
05/28/2021, 11:32 AMhttps://youtu.be/lK80dPcsNUg▾
raulraja
05/28/2021, 12:43 PMTower Guidev2
05/28/2021, 1:39 PMval action: Action = LoginAction(username, password)
viewmodel.react(action).collect {
reaction ->
when (reaction) {
LoginReaction -> println("\t\t ${reaction.reaction.output}")
Reaction1 -> println("\t\t ${reaction.reaction.output}")
Reaction2 -> println("\t\t ${reaction.reaction.output}")
Reaction3 -> println("\t\t ${reaction.reaction.output}")
}
}
In my viewmodel the react function I would have
fun react(action: Action): Flow<Reaction> = flow {
val reaction: Reaction = when (action) {
is LoginAction -> {
Val accessCode = callLoginApi(it.password, it.username)
LoginReaction(accessCode)
}
is ActionOne -> {
Reaction1(42L)
}
is ActionTwo -> {
Reaction2(CustomOutput(frank = 24L, homer = "Help!"))
}
is ActionThree -> {
Reaction3(CustomOutputTwo(geezer = 2442L, su = "Why"))
}
else -> TODO()
}
emit(reaction)
}
raulraja
05/28/2021, 5:30 PMTower Guidev2
06/01/2021, 9:30 AMType mismatch: inferred type is LoginReaction but Reaction<{A & LoginAction}> was expected
, when splitting your solution to fit how
I wish to design my Android App.
My issue is that each Android Activity will create multiple Action(s) and collect the related Reaction(s)
I've attempted to refactor your code as follows:-
In My Android ViewModel class I have created these two functions
fun <A : Action> react(action: A): Flow<*> = flow {
val reaction = when (action) {
is LoginAction -> {
val accessToken : String = LoginApi.login(action.password, action.password) // This is an Asynchronous RESTful API call
react(action) { LoginReaction(accessToken) } // COMPILE ERROR HERE!!!!!!!!!!!!
}
else -> TODO()
}
emit(reaction)
}
/**
* This is the motherload. This function ensures that a given [Action] [A]
* creates a [Flow] of [Reaction] for [R], a Reaction that only accepts actions of type [A]
*/
private inline fun <X : Action, Y : Reaction<X>> react(action: X, crossinline function: (X) -> Y): Reaction<X> {
return function(action)
}
I call my public react fucntion from my activity:-
val loginAction = LoginAction(UserName("test"), Password("test"))
viewmodel.react(loginAction).collectLatest {
println("debug $it")
}
where has my lack of Kotlin expertise let me down? 🤔raulraja
06/01/2021, 10:04 AMTower Guidev2
06/01/2021, 10:20 AMraulraja
06/01/2021, 10:51 AMTower Guidev2
06/01/2021, 10:57 AMraulraja
06/01/2021, 11:09 AMimport kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class Aardvark {
inline suspend fun <A : Action> react(action: A): Flow<Reaction<*>> =
when (action) {
is LoginAction -> react(action) { LoginReaction("UUID.randomUUID().toString()") }
is ActionOne -> react(action) { ReactionOne("UUID.randomUUID().toString()") }
else -> TODO()
}
inline suspend fun <X : Action, Y : Reaction<X>> react(action: X, crossinline function: (X) -> Y): Flow<Y> =
flow { emit(function(action)) }
}
Y
of Reaction in its type argument and inference then knows Y
above has to unify as a Reaction<*>
. Since Y
is declared as Reaction<X>
and *
in kotlin means DONT_CARE
then it unifies properly.
If you try to decompose in cases you will have to manually cast like in your case if you want to make it compile because Kotlin does not make an effort to unify generic type bounds with sealed hierarchies.A : Action
should work with just Action
no type args since A is not reified or used in a projection in the return typeTower Guidev2
06/01/2021, 12:20 PMraulraja
06/01/2021, 12:23 PMjulian
06/01/2021, 3:01 PMKotlin does not make an effort to unify generic type bounds with sealed hierarchies
fun react(action: Action): Flow<Reaction> = flow {
val reaction: Reaction = when (action) {
is LoginAction -> {
Val accessCode = callLoginApi(it.password, it.username)
LoginReaction(accessCode)
}
is ActionOne -> {
Reaction1(42L)
}
is ActionTwo -> {
Reaction2(CustomOutput(frank = 24L, homer = "Help!"))
}
is ActionThree -> {
Reaction3(CustomOutputTwo(geezer = 2442L, su = "Why"))
}
else -> TODO()
}
emit(reaction)
}
since the consumer of the flow is going to have to when-is
on the Reaction
s in the flow, either way.raulraja
06/01/2021, 4:53 PMsealed interface Exp<A>
data class NumberExp(val value: Int): Exp<Int>
data class StringExp(val value: String): Exp<String>
sealed interface Codec<A>
object IntCodec: Codec<Int>
object StringCodec: Codec<String>
fun <A> cantUnifyA(expr: Exp<A>): Codec<A> =
when (expr) {
is NumberExp -> IntCodec //Type mismatch: inferred type is IntCodec but Codec<A> was expected
is StringExp -> StringCodec //Type mismatch: inferred type is StringCodec but Codec<A> was expected
}
react(action) { reaction }
to create its flows all individuallyaction.react()
which on the base class would have returned Reaction<A>
where A is always matched by the members instead of using pattern matching.sealed interface Exp<A> {
fun codec(): Codec<A>
}
data class NumberExp(val value: Int): Exp<Int> {
override fun codec(): Codec<Int> = IntCodec
}
data class StringExp(val value: String): Exp<String> {
override fun codec(): Codec<String> = StringCodec
}
sealed interface Codec<A>
object IntCodec: Codec<Int>
object StringCodec: Codec<String>
fun <A> canUnifyA(expr: Exp<A>): Codec<A> =
expr.codec()
Codec<A>
refers to the same A
as Expr<A>
julian
06/01/2021, 5:28 PMraulraja
06/01/2021, 5:28 PM