title | description | canonical |
---|---|---|
Lazy Value | Data type for deferred computation in ReScript | /docs/manual/v11.0.0/lazy-values |
If you have some expensive computations you'd like to defer and cache subsequently, you can wrap it with lazy
:
<CodeTab labels={["ReScript", "JS Output"]}>
@module("node:fs") externalreaddirSync: string=>array<string> ="readdirSync"// Read the directory, only onceletexpensiveFilesRead=lazy({ Console.log("Reading dir") readdirSync("./pages") })
varFs=require("fs");varexpensiveFilesRead={LAZY_DONE: false,VAL: (function(){console.log("Reading dir");returnFs.readdirSync("./pages");})};
Check the JS Output tab: that expensiveFilesRead
's code isn't executed yet, even though you declared it! You can carry it around without fearing that it'll run the directory read.
Note: a lazy value is not a shared data type. Don't rely on its runtime representation in your JavaScript code.
To actually run the lazy value's computation, use Lazy.force
from the globally available Lazy
module:
<CodeTab labels={["ReScript", "JS Output"]}>
// First call. The computation happensConsole.log(Lazy.force(expensiveFilesRead)) // logs "Reading dir" and the directory content// Second call. Will just return the already calculated resultConsole.log(Lazy.force(expensiveFilesRead)) // logs the directory content
console.log(CamlinternalLazy.force(expensiveFilesRead));console.log(CamlinternalLazy.force(expensiveFilesRead));
The first time Lazy.force
is called, the expensive computation happens and the result is cached. The second time, the cached value is directly used.
You can't re-trigger the computation after the first force
call. Make sure you only use a lazy value with computations whose results don't change (e.g. an expensive server request whose response is always the same).
Instead of using Lazy.force
, you can also use pattern matching to trigger the computation:
<CodeTab labels={["ReScript", "JS Output"]}>
switchexpensiveFilesRead { | lazy(result) =>Console.log(result) }
varresult=CamlinternalLazy.force(expensiveFilesRead);
Since pattern matching also works on a let
binding, you can also do:
<CodeTab labels={["ReScript", "JS Output"]}>
letlazy(result) =expensiveFilesReadConsole.log(result)
varresult=CamlinternalLazy.force(expensiveFilesRead);console.log(result);
For completeness' sake, our files read example might raise an exception because of readdirSync
. Here's how you'd handle it:
<CodeTab labels={["ReScript", "JS Output"]}>
letresult=try { Lazy.force(expensiveFilesRead) } catch { | Not_found=> [] // empty array of files }
varresult;try{result=CamlinternalLazy.force(expensiveFilesRead);}catch(raw_exn){varexn=Caml_js_exceptions.internalToOCamlException(raw_exn);if(exn.RE_EXN_ID==="Not_found"){result=[];}else{throwexn;}}
Though you should probably handle the exception inside the lazy computation itself.