-
-
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
POC of unfoldable #1132
POC of unfoldable #1132
Conversation
Interesting! I like the idea of something like this. I'm a bit worried that the laws are too strict. For example, Is this worth it? |
Codecov Report
@@ Coverage Diff @@
## master #1132 +/- ##
=========================================
- Coverage 91.67% 91.6% -0.07%
=========================================
Files 240 244 +4
Lines 3617 3671 +54
Branches 61 67 +6
=========================================
+ Hits 3316 3363 +47
- Misses 301 308 +7
Continue to review full report at Codecov.
|
@julien-truffaut neat! Do you have a specific use-case that prompted you to submit this? I'm always interested in how people are using things. @non one possibility would be to start with these strict laws and later relax them if people find themselves wanting instances for types like |
@ceedubs not really, I wanted to implement |
What do you prefer:
|
I recall |
Sorry for the inactivity on this PR. I am not sure how to go from this, I made All ideas are welcome! We can also simply close this PR until we have a clearer view, I mainly opened it to start a discussion. |
I am not sure about the laws of |
That being said, if we go with def foo[F[_]: Unfoldable: Traverse] = ... // ambiguous Foldable[F] |
new DefaultRuleSet( | ||
name = "unfoldable", | ||
parent = None, | ||
// "noneConsistentWithDefault" -> forAll(() => laws.noneConsistentWithDefault[A]), |
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.
what's up here?
*/ | ||
def unfoldLeft[A, B](seed: B)(f: B => Option[(B, A)]): F[A] | ||
|
||
def none[A]: F[A] = |
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.
def empty
seems better to me. Do you foresee that conflicting with MonoidK
?
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.
yes, I would prefer to have unique function name for all cats typeclasses
def none[A]: F[A] = | ||
Unfoldable.DefaultImpl.none(this) | ||
|
||
def singleton[A](value: A): F[A] = |
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 this will wind up being pure
for Alternative
instances.
* - If `f(b)` is `None`, then `unfoldLeft(b)(f)` should be empty. | ||
* - If `f(b)` is `Some((b1, a))`, then `unfoldLeft(b)(f)` should consist of `a` appended by `unfoldLeft(b1)(f)` | ||
*/ | ||
def unfoldLeft[A, B](seed: B)(f: B => Option[(B, A)]): F[A] |
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.
seems like F[A]
may be a functor. Certainly if I have B, B => Option[(B, A)]
to get F[A]
and if I have A => C
then I can make F[C]
.
So, if for all F[A]
there exists B, B => Option[(B, A)]
to unfold to make F[A]
, then F[A]
is a functor.
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 F
is both a Foldable
and Unfoldable
then F
is a Functor
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.
In some cases, it could be useful to return the B
value at the end of the process, what do you think?
@typeclass trait Unfoldable[F[_]] { self => | ||
|
||
/** | ||
* Build an F[A] from a `seed` and a generating function `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.
it would be nice to have some stronger laws, even that might relate Unfoldable
to Foldable
or Functor
.
Such as, if F[_]: Unfoldable
and F[_]: Functor
then,
unfoldLeft(seed)(fn).map(g) == unfoldLeft(seed)(fn.andThen { case (b, a) => (b, g(a)) })`
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.
👍
534863e
to
0076901
Compare
case Nil => None | ||
case x :: xs => Some((xs, x)) | ||
} | ||
F.unfoldLeft(ga.reverse)(go) |
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.
Is there a reason we need to do the .reverse
here? I don't see any laws about maintaining order -- is this just so that Unfoldable[List].fromList(xs) <-> xs
?
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 right, I think it is an artifact from the impl where I made Unfoldable extends Foldable
. I guess we could express an order law if F
is also Foldable
This looks really cool. One thought that struck me is that right now I think this can only cover linear structures like List or Stream as opposed to structures like Tree with another branching factor. Also it can't really support structures that are always nonempty because when unfold returns none to stop the unfolding there is no value to put in the last layer. Is it worth thinking about how to abstract over these? Maybe something like S => (A, F[S]) for nonempty structures (like the way unfoldTree is implemented in Scalaz) and S => Option[(A, F[S])] for structures that could be empty? I think there is a connection to Free and Cofree here as well. This may be out of scope though. |
@adamgfraser really interesting idea, it would be great if |
I think we should revive this, especially given that we now require our |
So I think making |
Closing stale PRs. Feel free to reopen if there is interest to revive the effort. |
Implement Unfoldable as of https://github.com/purescript/purescript-unfoldable #872
I don't know if it is a good idea but if
Unfoldable
extendsFoldable
then we can lift all methods fromList[A] => List[A]
toF[A] => F[A]
e.g.prepend
,append
,filter
,concat
This PR is just a proof of concept, I mainly wish to get some feedbacks.