Skip to content
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

Either combine only use Semigroup[B] #1321

Closed
johnynek opened this issue Aug 22, 2016 · 7 comments
Closed

Either combine only use Semigroup[B] #1321

johnynek opened this issue Aug 22, 2016 · 7 comments

Comments

@johnynek
Copy link
Contributor

note combine(a, b) = a is a semigroup. We are basically forcing this "left-semigroup" on the left side.

I don't think this is a great idea at all. I would rather have:

def combine(that: Either[A, B])(implicit sa: Semigroup[A], sb: Semigroup[B]): Either[A, B] =
  (this, that) match {
    case (Right(lb), Right(rb)) => Right(sb.combine(lb, rb))
    case (Left(la), Left(ra)) => Left(sa.combine(la, ra))
    case (Right(_), l) => l
    case (r, Right(_)) => r
  }
@johnynek
Copy link
Contributor Author

#1034 and #888 are related.

@johnynek
Copy link
Contributor Author

so, it originally worked this way, I would say with Either we should revert this decision.

If you want a left semigroup, you can always make one:

implicit val leftSg: Semigroup[A] = new Semigroup[A] {
  def combine(a: A, b: A): A = a
}

But if you want to use Either for something other than error propagation, this is pretty terrible.

I have used either for approximation Monoids where there is also an exact algorithm. For small sizes, I use the Right side, but eventually I switch to Left to approximate (say using a Hyperloglog on the Left and a Set on the right). That case can be handled with a custom wrapper type on Either (and indeed must be or have an orphan since you need some logic to convert Right to Left).

But I still think if you want to only keep the left, you should set up a semigroup to do so.

@non
Copy link
Contributor

non commented Aug 22, 2016

If we wanted to go this way we could provide something like:

object ThrowableSemigroup extends Semigroup[Throwable] {
  def combine(x: Throwable, y: Throwable): Throwable = x
}

That way this change would be source compatible in almost all cases.

@yilinwei
Copy link
Contributor

I'm not sure about this. I feel that Either doesn't accumulate errors - if I wanted to accumulate errors I would use a Validated.

It also feels strange because then it becomes inconsistent with ap- combine = ap(that) { _ |+| _ } currently, which I think ought to be the default behaviour of combine.

@ceedubs
Copy link
Contributor

ceedubs commented Sep 30, 2018

@johnynek at this point it seems like it would be pretty disruptive to make this change. Are you okay with closing this issue out? I'm not sure about your specific use-case, but I think that the support for parTraverse has made moving between accumulating and non-accumulating sum types easier for most of my use-cases.

@johnynek johnynek closed this as completed Oct 1, 2018
@johnynek
Copy link
Contributor Author

johnynek commented Oct 1, 2018

I am not arguing for accumulating errors. For that I agree Validated works.

As I mentioned, this is for using Either as a generic sum, which it is.

But I guess if we want a generic sum type (like the example I mentioned where we have two representations that we can convert from one to the other but not vice versa) we are on our own.

We should document: Either is exclusively for monadic error accumulation in cats and any other usage of Either is likely to have confusing outcomes.

If you need a generic sum, that does not have monadic error semantics (Either this or that) you should make a new type because you may get confusing results.

@ceedubs
Copy link
Contributor

ceedubs commented Oct 1, 2018

@johnynek I see what you are saying. I agree that this is a bit unfortunate. When I made the change in #1034, Cats still had its separate Xor type which was meant to be the non-accumulating counterpart to Validated and I think that this change made more sense in that context. 😔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants