-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Add mapping functions for Pair and Triple #5216
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
base: master
Are you sure you want to change the base?
Conversation
Good Job |
*/ | ||
public fun <A, B, T> Pair<A, B>.mapFirst(transform: (A) -> T): Pair<T, B> { | ||
contract { | ||
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) |
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.
The transform
function is always invoked EXACTLY_ONCE
.
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.
The
transform
function is always invokedEXACTLY_ONCE
.
Good catch, will fix that.
* function. | ||
* @sample samples.misc.Tuples.pairMapFirst | ||
*/ | ||
public fun <A, B, T> Pair<A, B>.mapFirst(transform: (A) -> T): Pair<T, B> { |
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.
Why these functions are not inline
?
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.
Why these functions are not
inline
?
Only because I'm not sure when its suitable to inline functions in Kotlin. :-)
They can of course be inlined if you think that's a good idea? Is there any best practices documented regarding when to inline or not inline functions somewhere?
Really Kotlin needs this functions?
|
As a functional programmer I do think so. :-) This essentially makes the Pair/Triple a functor (or actually a bifunctor/trifunctor) and makes it much more convenient (and less error prone) to work with these types. Here is an example that I thinks makes it clear: // say we have a pair of Int to String
val pair: Pair<Int, String> = 42 to "The answer..."
// now we want to transform it into a Pair<Int,Int> instead. This is
// easy if we have the suggested mapping function.
val a: Pair<Int, Int> = pair.mapSecond { it.length }
// to do the same without the mapping functions you have to
// create a new pair from scratch which means needing to copying the first
// element of the pair.
val b: Pair<Int, Int> = pair.first to pair.second.length
val c: Pair<Int, Int> = Pair(pair.first, pair.second.length)
// note that we can't use the copy-method since the type has changed
val d: Pair<Int, Int> = pair.copy(pair.first, pair.second.length) // this won't compile
// also note that nothing is preventing us from swapping the values by mistake. This is impossible using the mapping function.
val bug: Pair<Int, Int> = Pair(pair.second.length, pair.first) IMHO, the winner measured on clarity and conciseness is definitely alternative |
a750c16
to
aaf34c3
Compare
KT-21648 Fixed
Tried to clean up some things in the branch and force pushed some stuff and the PR was closed. Sorry for this, don't know what happened. Reopened the PR. |
Does this pull request address a tangible issue? val bug: Pair<Int, Int> = Pair(pair.second.length, pair.first) This outcome arises from the misuse of primitive types and the utilization of an anonymous tuple, in my humble opinion. Could you provide a concrete real-life example? |
I agree that excessive use of Pair/Triple should be avoided in favour of more specialised and descriptive types if possible. Nevertheless the Kotlin stdlib does provide them and then should in my opinion try make it as ergonomic as possible to use them (when the user has decided it IS appropriate to use them). I can't remember the specific use case that led me to initiate this PR (it was a long time ago) but isn't the example I already gave is enough to see the benefits? I mean, mapping the contents of a "container like structure" without changing the structure itself is something we do all the time in FP. I can't see how the container like structure Pair/Triple is any different than a List, Result, Option or a Parser, they are all functors that can be mapped over. |
No, I don't detect
This is not the point. This is my point of view. |
How about this suggestion? "when you need to transform one element of a Pair (possibly into another type) without changing the other element, then use
Yes, the types differ, but only in how frequent they are used in a particular codebase. But regarding if a mapping-function is useful or not, they don't differ at all. To be able to use a data structure as a functor is always useful, imho. I can't really see the problem of introducing these functions to enable a more functional style of programming with Pair/Triple. |
grep.app finds several usages of this pattern, but not very much: |
The
Pair
andTriple
data types lacks a convenient way to map the individual components using a transformation function. The Result type already have map to transform the success value so it is already an established pattern in the stdlib.For Pair
mapFirst
andmapSecond
are added.For Triple
mapFirst
,mapSecond
andmapThird
are added.KT-21648 Fixed