-
-
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
Instances for Future #2334
Comments
Instances like |
My view is that any instance that is not lawful should not be in cats proper. In this case, you have treated Future as though it were a Sync instance, which it is not. You put in side effects, print, and had some idea in mind. I think you can come up with other similar examples with other types with call by name arguments. If users see a call by name parameter as an invitation to put a side effect we have a big problem since we often use them just for short circuiting. I argue that is the fault of the user. I advocate that we stick to the laws and argue for instances based on that. I don’t want cats to pick on some data types, whose instances are lawful, because they have other properties we don’t like. E.g. I don’t like that scala uses universal equality and hashing for Map by default. However, I still think Map should have a Functor instance, which is lawful. |
I agree in principle that it's "user error", but the error is committed when using the data types as intended. As a practical matter I think this hits beginners hardest, because experienced cats users are unlikely to use these data types at all. Conversations about So anyway I think a speed bump is warranted and I also vote for moving these to alleycats. While I'm thinking about it we should move the alleycats README onto the website so the warnings are louder. |
They were in alleycats, we had this discussion, we moved them back. Do we keep having this discussion every year or so and flip flopping? I guess anyone can do a PR and now cite the prior view they agree with and get two cats maintainers to ship it and we can switch. The catch is we can only flip flop on binary incompatible releases. PS: I really disagree with the Try example even more. cats-effect IO catches in similar places, but we are arguing to use that. throw is not a function, so trying to argue what is right from first principles does not make a lot of sense to me. |
One more thing: @tpolecat , I feel like your point is true about anything less than always doing pure functional programming, which I like, but cats has not been so dogmatic in the past. For instance, even in cats-effect we recently added non-referentially-transparent methods to allocate deffered instances: The motivation is performance and not forcing flatMaps where a user is already inside a delay/suspend operation. I think it is hard to not be an extremist: we have to have some answer other than: "all the way, all the time", but dealing with non-pure-FP is something scala programmers I think have to live with. Lastly, I am not arguing that you should write unsafe code, I am arguing that sometimes when writing safe code the compiler can't help you much, and you have to provide the guarantees yourself. When the compiler's guarantee come with large runtime costs (due to boxing, for instance), this problem is more acute. |
My thinking is that allowing the instances is the extremist position. I want to make things safer for people who aren't 100% there yet. |
I think the problem I have with for {
a <- Future(computeA)
b <- Future(computeB)
} yield a + b
// vs
val aF = Future(computeA)
val bF = Future(computeB)
for {
a <- aF
b <- bF
} yield a + b The differences here are observable even without side-effects simply because the computation time will be radically different. Normally I'm of the school of thought that doesn't consider CPU/memory/stack to be effects worth controlling, but So if we start from the premise that side-effect-free It should be removed. If we really really want to have it, then it should be in alleycats. |
@djspiewak I disagree with your example. If you have no side effects, those two are the same. If you are expecting runtime to be the same for all rewrites of the code, that is another property not referential transparency. Note, checking the clock is an effect, so if you are timing things, you are back to an effect space. I don't recall any time we have previously argued that substitutions must have the same runtime, if so, use of call-by-name is problematic in almost every case since it will often change the runtime of the code. |
@oscar-stripe So your argument then is that |
I think |
@tpolecat yeah, I agree with that take. Note: I work often in data-engineering areas, where we use Future to get concurrency to do computations in parallel: aggregate a tree of operations, not stateful side effects in many cases. (we actually use Twitter Future, which doesn't have the same |
@johnynek @tpolecat If they're all equivalent, then why have them all? Why are people wanting to use The point is that they're not equivalent. And critically, they're not equivalent in exactly the way that So either you think that A related argument is asking whether or not
To say this, you kind of need to define what you mean by "equal" in the context of your equational reasoning. Clearly, they are different expressions that will evaluate quite differently, so they can't be equal in every conceivable sense (unlike, say, |
Right, I mean precisely that. They are equivalent in the sense that they compute the same answers (assuming pure computations). And this is why they're useful! The fact that running time is unobservable means we know something extra, and we can use this to confidently refactor our program for better performance, knowing that it will compute the same answer. Applying the functor law to fuse map operations is a trivial example; refactoring to use Anyway my dog in this fight is that in practice As a meta observation I have never been explicitly warned to avoid simultaneous internet arguments with @djspiewak and @johnynek but it seems like I would know better by now ;-) |
Hey folks, I thought I should chime in on this one. First of all, I think you all have done a good job of re-framing the discussion away from the question of whether Personally, I don't think it's appropriate to remove (or demote) the future-based instances. If Cats doesn't work well with Probably the main driver of FP in Scala is the power of I'm not sure we gain very much from removing future-based instances, except possibly a lower support burden. I've been pretty inactive for a long time, so it's possible I'm not appreciating how heavy that burden has been recently. But historically, I feel like this has been OK. Anyway, this is just my 2¢. I'm interested to hear what other folks think. |
Well, in part. I believe my point was that either a)
I'm actually less worried about that. People are going to do what they're going to do. I want to encourage them to do the right thing by making that thing as easy as possible and discourage the wrong thing by throwing up some gentle barriers. As with players in video games, people will flow like water through whatever path is easiest. We as framework designers don't have control over all of that path (e.g. pre-existing code that depends on At the end of the day, we defined alleycats to be the project where unlawful instances go and cats-core to be the project where lawful and useful instances go.
While I agree that
I'm not sure it's so much about lower support burden as it is general framework design principles. At the end of the day, we have to subjectively decide where to draw lines on what is and is not part of cats. Many of these questions don't have objective answers. There might be objective evidence which points one way or another (as in this case), but how we synthesize and weigh that evidence is entirely a subjective process. There's almost no support burden for My argument is that |
I agree w/Daniel re: indemnifying ourselves or reducing our support burden … that's really not the point. All of us want users to have the best experience possible, and there is some disagreement on what that looks like. I will note that my opinions are colored by spending a lot of time in the Gitter channel and seeing beginners kind of marooned on Anyway I think this is probably kind of talked out. I certainly understand all the positions. |
Removing |
@tpolecat I'd be happy to help document the issues and solutions if you can help me collect them. |
Future
is largely known to not be referentially transparent. As it starts execution immediately and caches the result.While it may have been largely necessary for these instances to be in cats before cats-effect, with that solution coming in maturity it may be time for Future instances to be pushed to alleycats.
Imports
Example 1 - Prints Once on the first line
Example 2 - Prints twice on the second line
As you can see we have lost equational reasoning.
What are your thoughts on the future of
Future
?The text was updated successfully, but these errors were encountered: