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

Add forany and foranyPar to Traversable #392 #424

Open
wants to merge 1 commit into
base: series/1.x
Choose a base branch
from

Conversation

Badmoonz
Copy link

@CLAassistant
Copy link

CLAassistant commented Nov 22, 2020

CLA assistant check
All committers have signed the CLA.

@@ -122,6 +122,24 @@ trait Traversable[F[+_]] extends Covariant[F] {
}
}

def forany[G[+ _] : AssociativeEither : IdentityBoth : Covariant, A, B](fa: F[A])(f: A => G[B]): G[Option[B]] =
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IdentityEither is useless for these case, we don't want to use IdentityEither.none in case of empty traversable^ because it 'none' behaves like failure
So i requested IdentityBoth for any ; may be there should be separate typeclass in hierarchy that produces G[Any]?

@Badmoonz
Copy link
Author

I think we should give more attention for testing instances for effect types like ZIO, Future ...

FutureCommutativeEither for example seems broken

  Promise[Either[A, B]]().completeWith(fa.map(Left(_))).completeWith(fb.map(Right(_))).future

If any future fails faster than another one completes, whole result is failed, but by CommutativeEither laws it should select sucessful result;

ZIOCommutativeEither seems ok

sideeffffect
sideeffffect previously approved these changes Nov 22, 2020
Copy link
Member

@sideeffffect sideeffffect left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work, thank you @Badmoonz

@sideeffffect
Copy link
Member

FutureCommutativeEither for example seems broken

Wow, thanks for finding this. Any help with fixing the instance would be much appreciated 🙇

@sideeffffect
Copy link
Member

Also, as a stretch goal, do you think, you could implement analogous methods (forany1 and foranyPar1) on NonEmptyTraversable? 🙏 Those could return G[B] straight, without Option, since the collection is non-empty.

@sideeffffect
Copy link
Member

I see there's a conflict. Could you please resolve it @Badmoonz ? 🙏

@Badmoonz
Copy link
Author

I see there's a conflict. Could you please resolve it @Badmoonz ? 🙏

ok

@sideeffffect
Copy link
Member

You will also need to run fix to make the linter happy 😸

@Badmoonz
Copy link
Author

probably done :)

@sideeffffect
Copy link
Member

Do you think you could also try the NonEmptyTraversable variants? 🙏
#424 (comment)

@Badmoonz Badmoonz force-pushed the feature-392-traversable-forany branch from 607561f to dc64475 Compare November 22, 2020 19:13
@Badmoonz
Copy link
Author

Badmoonz commented Nov 22, 2020

Do you think you could also try the NonEmptyTraversable variants? 🙏
#424 (comment)

sure :)

within this issue( #392 )?

/**
* The `IdentityBoth` instance for `ZIO`.
*/
implicit def ZIOIdentityBoth[R, E]: IdentityBoth[({ type lambda[+a] = ZIO[R, E, a] })#lambda] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, could you do it the other way around? 🙏
The proper way is to have ZIOIdentityBoth in AssociativeBoth's companion object.

Copy link
Member

@sideeffffect sideeffffect left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please move ZIOIdentityBoth?

@Badmoonz Badmoonz force-pushed the feature-392-traversable-forany branch from 7fe3fda to b4df7ea Compare November 22, 2020 22:46
@@ -1200,14 +1200,6 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar
def both[A, B](fa: => Vector[A], fb: => Vector[B]): Vector[(A, B)] = fa.flatMap(a => fb.map(b => (a, b)))
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to delete this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ZIOAssociativeBoth can be fully replaced by ZIOIdentityBoth. Or should we call it ZIOCommutativeIdentityBoth? But it needs to be in the AssociativeBoth companion object 😉
More detailed explanation here: https://meta.plasm.us/posts/2019/09/30/implicit-scope-and-cats/

* `AssociativeEither[G].both` operation,
* returning `None` if the collection is empty.
*/
def forany[G[+_]: AssociativeEither: Covariant, A, B](fa: F[A])(f: A => G[B]): Option[G[B]] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this collectFirst? It applies the function f to each element in the structure and returns the first result that is a "success", where the meaning of success and failure is defined by the AssociativeEither instance?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's what I was going for! Thank you @adamgfraser , I didn't know it could have this name 👍
https://superruzafa.github.io/visual-scala-reference/collectFirst/ 👈 TIL
Although I would suspect we might need IdentityEither for this.

I was pushed to make a proposal for it, when I was looking at the signature for foreach and thought there should be a dual for it -- hence the name forany.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sideeffffect Wow! That Visual Scala resource is really cool!

I think we can get away with just having an AssociativeEither instance because None can serve as the default value if the structure is empty or if there is no successful value. However, I think we need to flip the order of the result types here so we return a G[Option[B]]. Otherwise we end up returning the last "failure" if the structure is non-empty and all of the results are failures.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Good point! But in that case, we would probably need IdentityBoth, to be able to create a G of None in case of an empty Traversable, right?
I've updated the ticket description #392
Please let me know, if you consider it accurate now 🙏

* `CommutativeEither[G].both` operation,
* returning `None` if the collection is empty.
*/
def foranyPar[G[+_]: CommutativeEither: Covariant, A, B](fa: F[A])(f: A => G[B]): Option[G[B]] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collectFirstPar?

* Compute each element of the collection
* with specified effectual function `f`
* and then reduces the results using the
* `AssociativeEither[G].both` operation,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `AssociativeEither[G].both` operation,
* `AssociativeEither[G].either` operation,

* Compute each element of the collection
* with specified effectual function `f`
* and then reduces the results using the
* `CommutativeEither[G].both` operation,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `CommutativeEither[G].both` operation,
* `CommutativeEither[G].either` operation,

/**
* Failing assertion, used for unexpected cases
*/
val unexpectedResult: Assertion[Any] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an existing nothing assertion that you can use for this.

Copy link
Contributor

@adamgfraser adamgfraser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of suggestions on naming. Otherwise looks great! Thanks for all your work on this!

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

Successfully merging this pull request may close these issues.

None yet

5 participants