-
Notifications
You must be signed in to change notification settings - Fork 67
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
Rework Foldable derivation #139
Conversation
a67916d
to
8223a1c
Compare
|
||
// The default `forall` is not lazy. | ||
override def forall[A](fa: F[A])(p: A => Boolean): Boolean = | ||
foldRight(fa, Eval.True)((a, lb) => lb.map(_ && p(a))).value |
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.
interesting. Did you find out because it broke the forallLazy
law here?
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.
Update, yes I confirmed that without these it broke the law. However the default forall
implementation did not break this law on List instance (yes, I commented out the override forall in the list instance). I am trying understand is the default implementation of forall
incorrect? or is it the foldRight derived here different?
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 figured it out. Our foldRight seems to be problematic (i.e. we shouldn't override the forall and exist, we should fix our foldRight
)
I added this test to our TraverseSuite
on your branch
test(s"$context foldRightLazy Not Empty") {
var i = 0
iList.foldRight(large, Eval.now(0)) { (_, _) =>
i += 1
Eval.now(1)
}.value
assert(i == 1)
}
This test failed , i
turns out to be 9999 the size of large.
It is a surprise that it's caught through foralllazy and existlazy laws, which I think is an issue in cats-laws. So I PRed one there (typelevel/cats#2817)
I haven't got the time to figure which part in our derivation that makes foldRight not lazy. Need to go afk for the night.
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.
Hmm, interesting. I don't understand entirely how it's supposed to work. How do you know that the first value is the final one so you stop calling foldRight?
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 figured it out. foldRight
was being called in the wrong order for HCons. I noticed this before but I missed the part where I have to add Eval.defer
to make it lazy / stack safe:
@kailuowang now foldRight for Snoc is not stack safe. I'm out of ideas... |
I have some ideas I want to try. will report back later. |
@joroKr21 , hmmm this pass locally on my macbook pro, which made it harder for me to debug the issue. how about on your machine? |
Ah sorry, I should have pushed a reliably failing test. We just need to modify Like this: test(s"$context.Foldable[Snoc].foldRight is stack safe") {
val large = List.fill(1)(Snoc.fromSeq(1 until n))
val actual = listSnoc.foldRight(large, Eval.Zero)((i, sum) => sum.map(_ + i))
val expected = 100 * n * (n - 1) / 2
assert(actual.value == expected)
} |
@kailuowang I am baffled. This definition (which is closer to the original) works: def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) = for {
unpacked <- Eval.now(F.unpack(fa))
b <- F.fh.unify.foldRight(unpacked._1, F.ft.foldRight(unpacked._2, lb)(f))(f)
} yield b It looks like |
b0a3407
to
460d45f
Compare
hmm, not sure about that so I created a test here Seems both are stack safe in the simple use case. |
We are using In fact if you put a Eval.defer inside Eval.flatMap it also SO. The following example SO def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) =
Eval.now(F.unpack(fa)).flatMap { case (fha, fhb) =>
F.fh.unify.foldRight(fha, Eval.defer(F.ft.foldRight(fhb, lb)(f)))(f) //Eval.defer added here just to test it.
} I couldn't get to the bottom of this one. I was trying to minimize it but didn't succeed. |
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.
Thanks so much!
* Make it more consistent with other derivations * Add more tests * Make sure it's stack safe * Make sure `forall` and `exists` are lazy
forall
andexists
are lazyBased on #138