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

Is partial evaluation of code possible? #528

Open
adamkl opened this issue Aug 17, 2020 · 4 comments
Open

Is partial evaluation of code possible? #528

adamkl opened this issue Aug 17, 2020 · 4 comments

Comments

@adamkl
Copy link

adamkl commented Aug 17, 2020

I do some of my development in Clojure/Script, and one of the killer features for me is the REPL.

I've tried to use Quokka to recreate that experience in JavaScript, and while its useful, the experience is somewhat lacking due to the fact that Quokka wants (needs?) to re-evaluate the entire file with every change/save.

With Clojure/Script, you can highlight and selectively evaluate code to change the actively running application context. This is especially useful when you are trying to develop code that needs to make calls to external resources (REST API, Database, etc). I know that Quokka has the option to only evaluate on save, and while that improves the situation, it still is somewhat lacking when compared to a more interactive REPL.

What I'd love to see is a Quokka session that works similarly to the workflow in this video: https://vimeo.com/230220635

Is this currently possible? If not, it would be awesome if this capability could be added to Quokka.

@smcenlly
Copy link
Member

The behavior of Quokka is exactly as you describe, which is to run your code using your entire file. This is by design and is intentionally different to a REPL. The idea behind this is to provide a reliable/repeatable means of executing your code (e.g. in comparison to pasting in snippets of code which are immutable and may change over time or be reset when you close your editor).

Having said that, we definitely understand your use case. It sounds like this issue could be addressed if Quokka could cache the results of certain results of your code (e.g. method calls return value is reused)?

@RDhar
Copy link

RDhar commented Nov 26, 2020

It sounds like this issue could be addressed if Quokka could cache the results of certain results of your code

This is huge, particularly with regards to caching of certain results. Often, I try to get a feel for API outputs in a sandbox but quickly run into rate-limiting with Quokka. Even run once/on save isn't ideal when you're trying to explore an endpoint.

Having the possibility to cache the results once and use it for subsequent execution would be a perfect use-case.

@smcenlly
Copy link
Member

While not a first class Quokka feature, you can do what you're asking for today. When you run your file with Quokka, by default Quokka re-uses the node process it starts to make subsequent executions much faster. You may set your API response using a global variable. For example:

function getApiResponse() {
  if (!global.myApiResponse) {
    global.myApiResponse = makeExpensiveServerCall();
  }
  return global.myApiResponse;
}

This doesn't mean that the API endpoint won't get hit multiple times because Quokka may recycle your process if there's a catastrophic error, but it will definitely limit remote execution most of the time.

We ourselves don't actually do this. For scenarios like the one that you're describing, we usually add logic to cache/read from a disk location in our scratch pad. Here's a generic template that you could use over and over again (maybe even create your own npm package or helper file for it):

function getApiResponse(param1, param2, param3, ... etc) {
  const fs = require('fs');
  const path = require('path');
  const crypto = require('crypto');

  // Generate a unique filename for the function arguments are provided (assumes no cyclical references)
  const args = JSON.stringify(Array.prototype.slice.call(arguments));
  const hash = crypto.createHash('md5').update(args).digest('hex');  
  const cacheFileName = path.join(require('os').tmpdir(), 'scratchFile', hash); // change scratch file name based on project or API call

  // Ensure the directory exists for our scratch file
  if (!fs.existsSync(path.dirname(cacheFileName))) {
    fs.mkdirSync(path.dirname(cacheFileName));
  }

  // If we already have a matching response, use it, otherwise make the call and cache it
  if (fs.existsSync(cacheFileName)) {
    return JSON.parse(fs.readFileSync(cacheFileName));
  } else {
    const response = makeExpensiveServerCall();
    fs.writeFileSync(cacheFileName, JSON.stringify(response));
    return response;
  }
}

@RDhar
Copy link

RDhar commented Nov 27, 2020

Many thanks, @smcenlly, this is well-neat and I really appreciate you sharing a well-commended code snippet as well to clarify its usage.

Definitely intend to make use of this valuable method going forward. 👍

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

No branches or pull requests

4 participants