Skip to content
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

Change name of memoize to cache #70

Merged
merged 1 commit into from Sep 1, 2015

Conversation

TheLudd
Copy link
Contributor

@TheLudd TheLudd commented Aug 19, 2015

Cache is a more accurate description of what the function does.

I recently watched a video with Douglas Crockford where he points out the difference between memoization and caching and I realized that this function is more like the latter. Subsequent calls to the original function will not necessarily yield the same result.

Cache is a more accurate description of what the function does.
@davidchambers
Copy link
Member

Subsequent calls to the original function will not necessarily yield the same result.

How so?

@TheLudd
Copy link
Contributor Author

TheLudd commented Aug 19, 2015

When an instance of a Future is forked twice there is no guarantee that the result will be the same. Is that not clear?

@davidchambers
Copy link
Member

When an instance of a Future is forked twice there is no guarantee that the result will be the same.

Yes, but we're not actually forking, are we? It seems to me we have:

Future.cache :: Future a b -> Future a b

I don't see how this breaks referential transparency (but it's quite possible I've misunderstood the function).

@TheLudd
Copy link
Contributor Author

TheLudd commented Aug 20, 2015

Lets say I have a file foo.txt that contains the text "foo" and I write this code:

var readFile = function(filename) {
  return new Future(function (reject, resolve) {
    fs.readFile(filename, 'utf-8', function(e, content) {
      if (e != null) {
        reject(e)
      } else {
        resolve(content)
      }
    })  
  }); 
}
var readFoo = readFile('foo.txt')
var readFooMemoized = Future.memoize(readFoo)

If I fork both readFoo and readFooMemoized the result will be "foo" from both. But if after this, the content of foo.txt is changed to to "bar" then forking readFoo will give me "bar" but forking readFooMemoized will still give me "foo".

@davidchambers
Copy link
Member

I see your point. Changing the name seems like a good idea.

Is R.once useful at all here?

@TheLudd
Copy link
Contributor Author

TheLudd commented Aug 20, 2015

Not really. There is no function that should only be called once. The memoized/cached future can be called several times with different reject/resolve functions. What is done depends on what has been done before.

But while we are at it. I am doubtful that this function is actually needed. I added it because I thought we did need it, but I haven't used it so far. We do do caching in our application but I am not sure that caching should be handled by Future. It seems like an idea that comes from the fact that promises do this.

@TheLudd
Copy link
Contributor Author

TheLudd commented Sep 1, 2015

merge?

@buzzdecafe
Copy link
Member

👍

buzzdecafe added a commit that referenced this pull request Sep 1, 2015
Change name of memoize to cache
@buzzdecafe buzzdecafe merged commit 2ff5e9f into ramda:master Sep 1, 2015
@joneshf
Copy link
Contributor

joneshf commented Sep 2, 2015

I think the problem is the conflation of types. Future a b encodes the effect of a value at a later point in time. It does not encode the effects of IO a, so using Future a b to read files breaks the contract of the data type.

If you want to combine effects like future values and reading files, you need a transformer for this kind of thing: FutureT a m b. The m tells what additional effects you're encoding. And since Future a b has no additional effects (aside from future values) we have FutureT a Id b == Future a b--as Id has no effects.

In your example, you're stating that you have a FutureT a Id b, but you're reading a file, so what you actually have is a FutureT a IO b. Given the effects of IO are untold, of course you can't expect memoization to be accurate. Whether the name is changed or not, it seems like there are other concerns as well.

@TheLudd
Copy link
Contributor Author

TheLudd commented Sep 16, 2015

@joneshf Is that really correct?

using Future a b to read files breaks the contract of the data type.

Where is this contract? I have not seen it but I have very much wanted to. Is there a formal definition of Future?

Future a b encodes the effect of a value at a later point in time

Not sure if I understand what you mean by this but is this not a description of Reader or IO which do lazy evaluation?

you need a transformer for this kind of thing: FutureT a m b

I had a discussion a while back with @scott-christopher about a Future.T but we concluded that it would not be possible to create such a type. The chain method does not seem doable.

so what you actually have is a FutureT a IO b

If by "IO" you refer to the IO monad i don't think this is correct in JavaScript. There is no way you can call runIO and by that point in time read the file and get the contents of it.

@TheLudd TheLudd deleted the memoize-is-cache branch September 16, 2015 07:29
@scott-christopher
Copy link
Member

I was playing around with the idea of FutureT again a couple of weeks ago and didn't come across whatever issue it was that we found with chain last time.

I've pushed up what I had so far to a branch if you're interested in having a look: master...scott-christopher:FutureT

All the original tests for Future in that branch pass when using FutureT(Identity), with the exception of the cache/memoize code (I got distracted with some other shiny thing before I got to the bottom of it).

The previous issues we were having may very well still be present in that branch, as I can't recall what they were. I haven't given it too much time beyond getting most of the tests to pass.

@TheLudd
Copy link
Contributor Author

TheLudd commented Sep 16, 2015

@scott-christopher master...scott-christopher:FutureT#diff-c5dd7575365ca756ae0970a47f408321R84

If m is an instance of Nothing or Either.Left the resolve function will never be called. That is basically the problem I think

@scott-christopher
Copy link
Member

Yep, that's right.

I wonder if that issue would still exist if we removed the notion of failure being bundled into Future in favour of a Future (Either a b), or FutureT Either. It would be nice (IMO) to strip back Future to be just about delayed computations and allow people to opt-in to error handling if necessary.

@TheLudd
Copy link
Contributor Author

TheLudd commented Sep 16, 2015

Sounds very interesting, created a dedicated thread...

@joneshf
Copy link
Contributor

joneshf commented Sep 16, 2015

using Future a b to read files breaks the contract of the data type.

Where is this contract? I have not seen it but I have very much wanted to. Is there a formal definition of Future?

I'm not sure if there is for this version of Future a b, I always assumed that was what the data type encoded. Scalaz' version does not encode failure (as I see this one might be changing to), so it's slightly different.

Future a b encodes the effect of a value at a later point in time

Not sure if I understand what you mean by this but is this not a description of Reader or IO which do lazy evaluation?

No, none of these things are the same.

  • Future a b - encodes future values that may either fail a or succeed b.
  • Reader a b - encodes values b that have access to an environment a.
  • IO a encodes values a that perform some sort of side effect on the world.

I've said it before, but I'll say it again.

Reader is Function.

Let me say that again.

Reader is Function.

In fact...

Reader is Function.

Oh, and by the way,

Reader is Function.

so what you actually have is a FutureT a IO b

If by "IO" you refer to the IO monad i don't think this is correct in JavaScript. There is no way you can call runIO and by that point in time read the file and get the contents of it.

While I don't doubt what you say, I also don't understand why it is true.

P.S. In case you forgot

Reader is Function.

@joneshf
Copy link
Contributor

joneshf commented Sep 16, 2015

I wonder if that issue would still exist if we removed the notion of failure being bundled into Future in favour of a Future (Either a b), or FutureT Either. It would be nice (IMO) to strip back Future to be just about delayed computations and allow people to opt-in to error handling if necessary.

YES!!

This is huge in making code reusable, understandable, and maintainable.

@TheLudd
Copy link
Contributor Author

TheLudd commented Sep 16, 2015

No, none of these things are the same

Reader and IO (in JavaScript, the ones I have seen) are implemented the same way, just with the addition of the specific ask instance on Reader. Reader may be used for injecting an environment variable but it is really just a delayed computation (and that fact I find truly awesome).

While I don't doubt what you say, I also don't understand why it is true.

Because JavaScript is non blocking thus IO operations towards the file system, databases, the network etc. requires the result to be provided in a continuation passing style which is not provided by IO but is by Future.

P.S. In case you forgot

Reader is Function

This I just find rude. Not sure what your are getting at.

@joneshf
Copy link
Contributor

joneshf commented Sep 17, 2015

No, none of these things are the same

Reader and IO (in JavaScript, the ones I have seen) are implemented the same way, just with the addition of the specific ask instance on Reader. Reader may be used for injecting an environment variable but it is really just a delayed computation (and that fact I find truly awesome).

They're implemented the same way because there is no type system to track effects. The semantics are totally different though.

P.S. In case you forgot

Reader is Function

This I just find rude. Not sure what your are getting at.

I apologize, it wasn't meant as a dig on you personally. I was attempting to be funny, but clearly it did not come across as such.

What it seems like to me, is that many people get confused about the purpose of Reader a b. There are plenty of tutorials about it that confuse even more. The fact that it exists in so many libraries in js at all I think is a bit confusing. Since Function is a first class object in js, you could polyfill the object and get the behavior you want. If that proposal to namespace in fantasy-land goes through, you could do it with more confidence.

@TheLudd
Copy link
Contributor Author

TheLudd commented Sep 17, 2015

I apologize, it wasn't meant as a dig on you personally. I was attempting to be funny, but clearly it did not come across as such.

Ok. cool =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants