-
Notifications
You must be signed in to change notification settings - Fork 448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CU-m3dyef Either, Option and Validated tap effect #2459
Conversation
Task linked: CU-m3dyef Review User requested functionality |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one potential suggestion. Otherwise looks good 👍
public inline fun tapLeft(f: (A) -> Unit): Either<A, B> = | ||
when (this) { | ||
is Left -> { | ||
f(this.value) | ||
this | ||
} | ||
is Right -> this | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also express this with a scoping function:
public inline fun tapLeft(f: (A) -> Unit): Either<A, B> = | |
when (this) { | |
is Left -> { | |
f(this.value) | |
this | |
} | |
is Right -> this | |
} | |
public inline fun tapLeft(f: (A) -> Unit): Either<A, B> = apply { | |
if(this is Left) { | |
f(value) | |
} | |
} |
Is it easier to read? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this feels better it would apply to the other taps 🙂 Otherwise happy to be ignored 😅 #notablocker
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pablisco apply it's fine since I suppose the lambda has no cost, and its body is inlined since it's flagged as invoked once in its contract. I speculate it does not need to capture the state for the lambda. I'd be curious to see the resulting bytecode because the when
statement I wrote has no allocations and most likely results in a table-switch indexed lookup.
In whatever case, It's not important to me. If you want to suggest some changes to the impl in the same branch, we can apply them before merging; I'm sure whatever users write in the lambda is gonna be more costly than any optimizations :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raulraja Actually, now I think of it we could add the option to add effects for both sides of the bifunctor and make use of existing operations 🙂
public inline fun onLeft(f: (A) -> Unit): Either<A, B> =
onEither(left = f, right = {})
public inline fun onRight(f: (B) -> Unit): Either<A, B> =
onEither(left = {}, right = f)
public inline fun onEither(left: (A) -> Unit, right: (B) -> Unit): Either<A, B> =
apply { fold(left, right) }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's probably allocating for each one of those lambdas? If that is the case, I'd rather have an alloc free impl that is less readable as we have no context as to how these functions will be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is probably right. One option may be to use a decoy for the empty lambda if we don't have something like this already. Need to check the byte code :)
As I said, unless someone else sees this useful, we can explore it later on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think apply
is confusing here since apply
is typically for mutable instantiation logic, like builder syntax.
Which this definitely doesn't do.
I think the when
code Raul has is the simplest 👍
Ah, I got something else. 😅 Would it make sense to call these |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the convention we've been using is ifLeft
, ifRight
, ifNone
, etc.
onX
seems to imply executing a side-effect, which is actually why this naming was chosen in ReactiveX IIRC.
EDIT:
public inline fun <C> fold(ifLeft: (A) -> C, ifRight: (B) -> C): C
https://github.com/arrow-kt/arrow/blob/main/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt#L761
public inline fun tapLeft(f: (A) -> Unit): Either<A, B> = | ||
when (this) { | ||
is Left -> { | ||
f(this.value) | ||
this | ||
} | ||
is Right -> this | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think apply
is confusing here since apply
is typically for mutable instantiation logic, like builder syntax.
Which this definitely doesn't do.
I think the when
code Raul has is the simplest 👍
Addresses #2416 and #2410
In the second case in #2410
tap
is the same asforeach
and there is alsotapNone