-
-
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
Changes from 38 commits
0ea4ac0
0cd4eb9
f9a239a
965f9c2
d3a803a
1ab4f47
2164b51
7f1f6c7
75a656a
69c6374
fa8a731
2aeddd7
aa49de9
d308cf7
148c113
0ffbc55
23cb589
e0069a8
376a5c6
d673655
8666c64
74b0228
dfa8bd3
903694d
821424d
a1cdf2d
cdc6586
3e705d4
5f4d41f
cd2e1f5
5d9f0f7
f70fe6f
04722f0
9568933
9666a0c
23a6ca1
d2037e6
11b397c
89cab23
e2984b4
ea38cd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package cats | ||
|
||
import cats.kernel.CommutativeMonoid | ||
import simulacrum.typeclass | ||
import cats.instances.set._ | ||
import cats.instances.long._ | ||
/** | ||
* `UnorderedFoldable` is like a `Foldable` for unordered containers. | ||
*/ | ||
@typeclass trait UnorderedFoldable[F[_]] { | ||
|
||
def unorderedFoldMap[A, B: CommutativeMonoid](fa: F[A])(f: A => B): B | ||
|
||
def unorderedFold[A: CommutativeMonoid](fa: F[A]): A = | ||
unorderedFoldMap(fa)(identity) | ||
|
||
def toSet[A](fa: F[A]): Set[A] = | ||
unorderedFoldMap(fa)(a => Set(a)) | ||
|
||
/** | ||
* Returns true if there are no elements. Otherwise false. | ||
*/ | ||
def isEmpty[A](fa: F[A]): Boolean = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be implemented with foldMap (map to true, do the commutative “or” monoid |
||
exists(fa)(Function.const(true)) | ||
|
||
def nonEmpty[A](fa: F[A]): Boolean = | ||
!isEmpty(fa) | ||
|
||
/** | ||
* Check whether at least one element satisfies the predicate. | ||
* | ||
* If there are no elements, the result is `false`. | ||
*/ | ||
def exists[A](fa: F[A])(p: A => Boolean): Boolean = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be implemented with unorderedFoldMap |
||
unorderedFoldMap(fa)(a => Eval.later(p(a)))(UnorderedFoldable.commutativeMonoidEval(UnorderedFoldable.orMonoid)) | ||
.value | ||
|
||
/** | ||
* Check whether all elements satisfy the predicate. | ||
* | ||
* If there are no elements, the result is `true`. | ||
*/ | ||
def forall[A](fa: F[A])(p: A => Boolean): Boolean = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be implemented with foldMap using and monoid. |
||
unorderedFoldMap(fa)(a => Eval.later(p(a)))(UnorderedFoldable.commutativeMonoidEval(UnorderedFoldable.andMonoid)) | ||
.value | ||
|
||
/** | ||
* The size of this UnorderedFoldable. | ||
* | ||
* This is overriden in structures that have more efficient size implementations | ||
* (e.g. Vector, Set, Map). | ||
* | ||
* Note: will not terminate for infinite-sized collections. | ||
*/ | ||
def size[A](fa: F[A]): Long = unorderedFoldMap(fa)(_ => 1L) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could implement the |
||
|
||
object UnorderedFoldable { | ||
private val orMonoid: CommutativeMonoid[Boolean] = new CommutativeMonoid[Boolean] { | ||
val empty: Boolean = false | ||
|
||
def combine(x: Boolean, y: Boolean): Boolean = x || y | ||
} | ||
|
||
private val andMonoid: CommutativeMonoid[Boolean] = new CommutativeMonoid[Boolean] { | ||
val empty: Boolean = true | ||
|
||
def combine(x: Boolean, y: Boolean): Boolean = x && y | ||
} | ||
|
||
private def commutativeMonoidEval[A: CommutativeMonoid]: CommutativeMonoid[Eval[A]] = | ||
new EvalMonoid[A] with CommutativeMonoid[Eval[A]] { val algebra = Monoid[A] } | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package cats | ||
|
||
import simulacrum.typeclass | ||
|
||
/** | ||
* `UnorderedTraverse` is like a `Traverse` for unordered containers. | ||
*/ | ||
@typeclass trait UnorderedTraverse[F[_]] extends UnorderedFoldable[F] { | ||
def unorderedTraverse[G[_]: CommutativeApplicative, A, B](sa: F[A])(f: A => G[B]): G[F[B]] | ||
|
||
def unorderedSequence[G[_]: CommutativeApplicative, A](fga: F[G[A]]): G[F[A]] = | ||
unorderedTraverse(fga)(identity) | ||
} |
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 can be implemented with foldMap
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 wonder if actually we should take an Order here. This is using universal hashing and i hate to bake that into a new typeclass.
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.
If we take an Order we could make it a
SortedSet
, not sure if that really makes sense. I agree with your sentiment, but not sure how to deal with it exactly. Maybe take aHash[A]
? I dunno.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 agree that having
UnorderedFoldable
taking aOrder
doesn't make much sense.The whole point of this PR is to support some principled functionalities for
Set
andMap
, which are based off universal hashing I am not sure if we can get away from it.If we really want
UndorderedFoldable
as a principled abstraction, we should probably only support theSet
andMap
in Dogs.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.
we could make this
toList
with the only law being aboutcontains
andEq[A]
. It would beO(N^2)
to check the law, but that is probably fine.I just hate sneaking universal hashing or universal equality in.
I don't see the use or
Order[A]
as not making sense, since it is the collection that is unordered here, not the typeA
. So, I don't want to get those two confused.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.
+1 on this solution.
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'm not sure
toList
really makes sense, given the fact that we only want to fold using aCommutativeMonoid
whichList
clearly is not. Maybe we just get rid of this method all together? I really wish we had aHashSet
withcats.kernel.Hash
.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 agree. At the very least this is not stable enough to be included in 1.0.0. I propose we remove this and wrap this PR up.