Add bimap method to Either

#1

I was a bit suprised to learn this doesn’t exist yet, mapping over both the left side and the right side of Either in one go:

val either: Either[Int, String] = ???
val after: Either[String, Int] = either.bimap(_.toString, _.toInt)

I think it would be nice to have this as part of the standard library and I would be happy to implement it!
Is there a reason this is not implemented yet, or was it just not brought up yet? I couldn’t find any discussion about it so far.

Looking forward to hear opinions on it!

4 Likes
#2

There’s Either.fold method existing for a very long time. Probably that was enough:

val either: Either[Int, String] = ???
val bimapped: Either[String, Int] = either.bimap(_.toString, _.toInt)
val folded: Either[String, Int] = either.fold(v => Left(v.toString), v => Right(v.toInt))

It’s longer, but still not very long.

Certainly bimap would make some code more elegant and readable, but I don’t think if it would make big difference overall. I’m not against it, though.

There’s strong push against adding new methods to collections, because typically we want all collections to share as much methods as possible, so adding method to one collection means adding it to all collections. OTOH, Either is not a collection and it doesn’t share compilcated supertypes with other classes so it doesn’t suffer from such problems.

#3

Thanks for your feedback!

Yes, I’m aware. bimap would be a special case of fold. I wouldn’t mind too much if I could do either of those:

val result: Either[String, Int] = either.fold(Left(_.toString), Right(_.toInt))

or

val result: Either[String, Int] = either.fold(Left(f(_)), Right(g(_)))

But having to write the full lambda is quite tedious IMO.

I think the upsides of having the method would outweigh the downsides since it’s just adding a single method, but I could be wrong.

Certainly bimap would make some code more elegant and readable, but I don’t think if it would make big difference overall. I’m not against it, though.

True, it’s not very often this is useful. Most likely that’s why it wasn’t brought up yet.

There’s strong push against adding new methods to collections, because typically we want all collections to share as much methods as possible, so adding method to one collection means adding it to all collections.

I also thought about whether it’s possible to add it to Try and Future (which are not collections either, but I assume the same principles apply), but it’s not a good idea IMO, as the first parameter would be Throwable => Throwable (which is not really map operation).

#4

Another alternative:

val after = either.map(_.toInt).swap.map(_.toString).swap
#5

I have mixed feelings about this. On the one hand I use bimap a lot and having it in stdlib is a good step forward, but on the other hand I find stdlib unusable without import cats.syntax.all._ anyway, so this single method won’t help much…