-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 UnorderedFoldable and UnorderedTraverse #1981
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1981 +/- ##
==========================================
- Coverage 95.25% 95.14% -0.11%
==========================================
Files 305 311 +6
Lines 5179 5252 +73
Branches 127 131 +4
==========================================
+ Hits 4933 4997 +64
- Misses 246 255 +9
Continue to review full report at Codecov.
|
81feb52
to
74b0228
Compare
I like the idea of introducing these two new type classes. |
Good question! I don't think there's a huge benefit apart from deduplication. |
You probably guessed what I would say next. They are useful but
Sorry, I am biased towards limiting scope in 1.0. |
Can we make these two separate from the |
Can we rebase master? I am curious if this one is bin compat with 1.0.0-RC1, Update: I can see benefits of having |
|
||
/** | ||
* `UnorderedTraverse` is like a `Traverse` for unordered containers. In addition to the traverse and sequence | ||
* methods it provides nonEmptyTraverse and nonEmptySequence methods which require an `Apply` instance instead of `Applicative`. |
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.
This line of docs seems inaccurate?
*/ | ||
def forall[A](fa: F[A])(p: A => Boolean): Boolean = | ||
unorderedFoldMap(fa)(p)(UnorderedFoldable.andMonoid) | ||
} |
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.
This could implement the size
method from Foldable
as well, I think.
*/ | ||
def forallEmpty[A](fa: F[A], p: A => Boolean): Boolean = { | ||
!F.isEmpty(fa) || F.forall(fa)(p) | ||
} |
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.
Could it be made to be such that existsLazy
and forallLazy
are laws for UnorderedFoldable
and not just Foldable
?
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 lazyness would require foldRight
, no?
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 believe you can do it with Eval
: see my duplicative attempt at this type class.
def exists[A](fa: F[A])(p: A => Boolean): Boolean =
foldMapUnordered(fa)(a => Eval.later(p(a)))(new CommutativeMonoid[Eval[Boolean]] {
def empty = Eval.False
def combine(x: Eval[Boolean], y: Eval[Boolean]): Eval[Boolean] =
x.flatMap(xv => if (xv) Eval.True else y)
}).value
and dually for forall
.
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.
You're absolutely right, this is great! Thank you so much!
|
||
import cats.data.Nested | ||
|
||
trait UnorderedTraverseLaws[F[_]] extends UnorderedFoldableLaws[F] { |
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.
This could/should extend FunctorLaws[F]
and assert some things about the compatibility of UnorderedTraverse
and Functor
; for instance, I would expect unorderedSequence
to be unorderedTraverse
on identity (which assumes that Eq
for these instances respects unorderedness if it's not also a Traverse
, I think)
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.
Hmmm, I'm not sure. That would imply that Set
would be a valid Functor
which I believe it is not. We could add a function like unorderedMap
that's just unorderedTraverse
with Id
.
Thanks for your review by the way, it's been very helpful! :)
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.
No, you're right; I got a little bit overenthusiastic about drawing these parallels.
I think unorderedMap
is kinda dubious, because the difference isn't even lack of order (which is unobservable in Functor#map
already!), but that the operation can "drop" values, losing fusion, and therefore isn't a (categorical) Functor
. I don't know what laws for unorderedMap
would be, other than "it's the same as traversing with Id
".
… into add-unordered-classes
5523596
to
f70fe6f
Compare
} | ||
|
||
def toSetRef[A](fa: F[A]): IsEq[Set[A]] = | ||
F.unorderedFoldMap(fa)(a => Set(a)) <-> F.toSet(fa) |
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.
this could be
val lst1 = F.unorderedFoldMap(fa)(a => List(a))
val lst2 = F.toList(fa)
def contains[A: Eq](a: A): Boolean = lst1.exists(Eq[A].eqv(_, a))
val allCont = list2.map(contains)
val allTrue = lst.map(_ => true)
allCont <-> allTrue
In this way, we have a law that only needs Eq, not unusual, and the typeclass does not require any Order or Hash.
if we have an Order[A]
. So we use an Order
to check the law more quickly, or we could
… into add-unordered-classes
d0ac64e
to
e2984b4
Compare
As discussed in #1927, together with #1972 and #1966 this should fix #1831.
In this PR, I remove the
Traverse
andFoldable
instances fromMap
andSet
and replace them withUnorderedTraverse
andUnorderedFoldable
. I think we could letFoldable
andTraverse
extend these.This PR depends on #1927.