-
-
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 a Continuation data type #1400
Conversation
|
||
object Continuation { | ||
def pure[O] = new PureBuilder[O] | ||
class PureBuilder[O] { |
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.
final
and private[Continuation]
? The former for sure, the latter mostly because I saw that's how we've been doing it: https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/data/OptionT.scala#L167
import java.io.Serializable | ||
import scala.annotation.tailrec | ||
|
||
sealed abstract class Continuation[O, +I] extends Serializable { |
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.
extends Product with Serializable
Also Scaladoc saying Continuation[O, I]
is (I => O) => O
. Seeing the type parameters order reversed confused me for a second before I realized it was probably for the Monad
instance
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 was also going to suggest a signature of Continuation[-I, +O]
, unless there's some reason for the current order of types, and their variances
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.
Assuming my algebra is right I think O
could also be made covariant like you said. A separate but related question is if we want this to be variant at all since that comes with all the gotchas associated with variance and subtyping in Scala. I would vote no.
I'm guessing the order is because it makes partial application look a bit cleaner since the Monad
instance is free in I
as opposed to R
. with Continuation[I, O]
you would have Monad[Continuation[?, O]]
as opposed to Monad[Continuation[O, ?]]
.
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.
Good point, variances can turn into land-mines. Far easier to add them later if there is a reason, than try to remove them after the fact.
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, O
is invariant. Note that it appears in the output, so it is either invariant or covariant. But it also appears in the input of the function, which makes it invariant or contravariant (see the apply
method), so it is invariant.
A more direct way to see it: if you have a function (I => O) => O
and say, O = String
. Now due to covariance, I want to treat it as (I => Any) => Any
can I do this? It is not clear at all how since internal to a function (I => String) => String
we could make use of the String structure, but when I am given a function to Any, I can't do that.
So in fact, Continuation[O, +I]
is the correct variance.
Now, about ordering of the parameters, I agree it is not the most intuitive order, but due to the the 2712 fix, right to left is the standard order searched when looking for single parameter type classes, so I think we are better off keeping the parameter there.
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.
about adding co-variance, I'd really like to keep it. Contravariance causes the most problems, I think, but covariance is very useful and common especially in container code.
Can you point to the problem that covariance causes?
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 unaware of any particular problems relating to covariance. Since I
plays the role of input type in I => O
, it seemed as though it would make sense to have it be -I
, analogous to Function[-I, +O]
, however that doesn't take into account the ways it is 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.
When you use a contravariant type in a contravariant position the variance is flipped.
So Continuation
has an apply
method as follows:
def apply(fn: I => O): O
In the context of Function1[I, O]
the I
is contravariant (as you point out). However this function itself is used in a contravariant position (an input parameter) to the apply
method. This means that in the context of Continutation
I
is covariant (a contravariant type used in contravariant position is treated as covariant).
Does that make sense to you? It might help to think about the relationship between List[A]
and its map
method (e.g. def map[A, B](f: A => B): List[B]
).
EDIT: Turns out @johnynek said the same thing below. Jinx!
} | ||
|
||
private case class Const[O, I](i: I) extends Continuation[O, I] | ||
|
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.
Don't know if also marking these as final
does anything but maybe for style consistency?
} | ||
|
||
sealed abstract class Fn[-A] | ||
case class ScalaFn[A](fn: A => O) extends Fn[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.
extends Product with Serializable
, final
case classes
} | ||
case class Complete(get: O) extends Result | ||
case class ContResult[I](runfn: (I => O) => O, fn: Fn[I]) extends Result { | ||
def get: O = fn match { |
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.
extends Product with Serializable
, final
case classes?
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.
the parent class is sealed
, so case classes are already final, aren't they?
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.
Yeah but it still bothers me from a style perspective ;)
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.
do we really need to do needless work just for style? There are a lot of classes here all hidden inside a private class.
|
||
Arbitrary(Gen.oneOf(I.arbitrary.map(call(_)), O.arbitrary.map { o => const(o) })) | ||
} | ||
implicit def arbCont[O, I](implicit I: Arbitrary[I], fn: Arbitrary[(I => O) => O]): Arbitrary[Continuation[O, I]] = |
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.
Should this go in cats.laws.discipline.arbitrary
So, it is On Tue, Oct 4, 2016 at 8:40 AM Erik Erlandson notifications@github.com
|
@adelbertc can you explain the |
Codecov Report
@@ Coverage Diff @@
## master #1400 +/- ##
==========================================
+ Coverage 91.68% 91.77% +0.08%
==========================================
Files 240 241 +1
Lines 3610 3648 +38
Branches 63 60 -3
==========================================
+ Hits 3310 3348 +38
Misses 300 300
Continue to review full report at Codecov.
|
case _ => | ||
// This is not stack safe, we could keep finding continuations | ||
// the stack depth will scale as the number of inner continuations, | ||
// not flatMaps (which you can see |
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 comment didn't quite get finished.
@johnynek @adelbertc scala> sealed trait Foo
defined trait Foo
scala> case object Bar extends Foo
defined object Bar
scala> case object Baz extends Foo
defined object Baz
scala> List(Bar, Baz)
res0: List[Product with Serializable with Foo] = List(Bar, Baz) // Product with Serializable with Foo Marking case classes as final only ensures that they cannot be extended, which is good since this usually breaks compiler-generated method implementations like I'm sure you both knew these things already, but there is no other benefit as far as I'm aware. I'd be inclined to suggest that |
@DavidGregory084 Ahh, I had not seen the explanation for adding I agree it is nice for public classes. |
@johnynek do you need any help to complete this? |
I'm not sure there is any interest in merging. The requested edits are
mechanical, but I don't want to spend time on them if actually we won't
merge.
…On Tue, Dec 13, 2016 at 00:50 Alexey Romanchuk ***@***.***> wrote:
@johnynek <https://github.com/johnynek> do you need any help to complete
this?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1400 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAEJdi1lClB0EkNjiuLRe8xNw7XI-sGOks5rHniPgaJpZM4KNSUt>
.
|
I'm still interested in this, however my interest has become mostly abstract. I now think that free-monads may be sufficient for my purposes. In any case, my project is completely experimental (and currently inactive) and I could just as easily include this prototype in my own code if I want to play with it some more. I don't think my side projects are sufficient justification to merge something that there's ambivalence about supporting :) |
@johnynek I'm tempted to just close this. There doesn't seem to be that much interest in it, overall, which is unfortunate but understandable. Opinion? |
@djspiewak Don't. |
I think we can merge it, maybe with a bit of polish.
If we want to make it fully stack safe, I think we have to introduce a
stack safe function type, and use that as the continuation.
…On Fri, Jun 2, 2017 at 18:24 Philip Polkovnikov ***@***.***> wrote:
@djspiewak <https://github.com/djspiewak> Don't.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1400 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAEJdnQcydxQ_rTBnASXHRIzwwc7OzBBks5sAN_8gaJpZM4KNSUt>
.
|
I think a non stack safe variant is probably ok. I mean, the variant you've implemented (which is basically Free of Cont) is stack safe on binds, just not on suspends. Which is fine, because callcc is not something I would expect in a continuous loop. |
addresses #700
This does not add
ContinuationT[M[_], O, I]
which can also be implemented for any monadM[_]
. This should not be too hard, but will add a performance hit relative to the non-transformer version.I may add a follow up that has
ContinuationT
if we feel that is useful. The main trick is to usetailRecM
rather than the tailrec loop I have here.