Skip to content

move Lazy module to Stdlib #7399

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

Merged
merged 1 commit into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

- Improve error message for missing value when the identifier is also the name of a module in scope. https://github.com/rescript-lang/rescript/pull/7384
- Upgrade Flow parser to 0.267.0. https://github.com/rescript-lang/rescript/pull/7390
- Move `Lazy` module to Stdlib. https://github.com/rescript-lang/rescript/pull/7399

# 12.0.0-alpha.11

Expand Down
5 changes: 4 additions & 1 deletion lib/es6/Stdlib.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function assertEqual(a, b) {
RE_EXN_ID: "Assert_failure",
_1: [
"Stdlib.res",
119,
120,
4
],
Error: new Error()
Expand Down Expand Up @@ -49,6 +49,8 @@ let $$Intl;

let $$JSON;

let Lazy;

let List;

let $$Math;
Expand Down Expand Up @@ -133,6 +135,7 @@ export {
Int,
$$Intl,
$$JSON,
Lazy,
List,
$$Math,
Null,
Expand Down
9 changes: 9 additions & 0 deletions lib/es6/Lazy.js → lib/es6/Stdlib_Lazy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

import * as Primitive_lazy from "./Primitive_lazy.js";

let make = Primitive_lazy.from_fun;

let get = Primitive_lazy.force;

let isEvaluated = Primitive_lazy.is_val;

let Undefined = Primitive_lazy.Undefined;

let force = Primitive_lazy.force;
Expand All @@ -15,6 +21,9 @@ let from_val = Primitive_lazy.from_val;
let is_val = Primitive_lazy.is_val;

export {
make,
get,
isEvaluated,
Undefined,
force,
force_val,
Expand Down
5 changes: 4 additions & 1 deletion lib/js/Stdlib.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function assertEqual(a, b) {
RE_EXN_ID: "Assert_failure",
_1: [
"Stdlib.res",
119,
120,
4
],
Error: new Error()
Expand Down Expand Up @@ -49,6 +49,8 @@ let $$Intl;

let $$JSON;

let Lazy;

let List;

let $$Math;
Expand Down Expand Up @@ -132,6 +134,7 @@ exports.Float = Float;
exports.Int = Int;
exports.$$Intl = $$Intl;
exports.$$JSON = $$JSON;
exports.Lazy = Lazy;
exports.List = List;
exports.$$Math = $$Math;
exports.Null = Null;
Expand Down
9 changes: 9 additions & 0 deletions lib/js/Lazy.js → lib/js/Stdlib_Lazy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

let Primitive_lazy = require("./Primitive_lazy.js");

let make = Primitive_lazy.from_fun;

let get = Primitive_lazy.force;

let isEvaluated = Primitive_lazy.is_val;

let Undefined = Primitive_lazy.Undefined;

let force = Primitive_lazy.force;
Expand All @@ -14,6 +20,9 @@ let from_val = Primitive_lazy.from_val;

let is_val = Primitive_lazy.is_val;

exports.make = make;
exports.get = get;
exports.isEvaluated = isEvaluated;
exports.Undefined = Undefined;
exports.force = force;
exports.force_val = force_val;
Expand Down
16 changes: 8 additions & 8 deletions packages/artifacts.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ lib/es6/Jsx.js
lib/es6/JsxDOM.js
lib/es6/JsxDOMStyle.js
lib/es6/JsxEvent.js
lib/es6/Lazy.js
lib/es6/Obj.js
lib/es6/Pervasives.js
lib/es6/Primitive_array.js
Expand Down Expand Up @@ -178,6 +177,7 @@ lib/es6/Stdlib_Intl_Segmenter.js
lib/es6/Stdlib_Intl_Segments.js
lib/es6/Stdlib_Iterator.js
lib/es6/Stdlib_JSON.js
lib/es6/Stdlib_Lazy.js
lib/es6/Stdlib_List.js
lib/es6/Stdlib_Map.js
lib/es6/Stdlib_Math.js
Expand Down Expand Up @@ -287,7 +287,6 @@ lib/js/Jsx.js
lib/js/JsxDOM.js
lib/js/JsxDOMStyle.js
lib/js/JsxEvent.js
lib/js/Lazy.js
lib/js/Obj.js
lib/js/Pervasives.js
lib/js/Primitive_array.js
Expand Down Expand Up @@ -351,6 +350,7 @@ lib/js/Stdlib_Intl_Segmenter.js
lib/js/Stdlib_Intl_Segments.js
lib/js/Stdlib_Iterator.js
lib/js/Stdlib_JSON.js
lib/js/Stdlib_Lazy.js
lib/js/Stdlib_List.js
lib/js/Stdlib_Map.js
lib/js/Stdlib_Math.js
Expand Down Expand Up @@ -809,12 +809,6 @@ lib/ocaml/JsxEvent.cmi
lib/ocaml/JsxEvent.cmj
lib/ocaml/JsxEvent.cmt
lib/ocaml/JsxEvent.res
lib/ocaml/Lazy.cmi
lib/ocaml/Lazy.cmj
lib/ocaml/Lazy.cmt
lib/ocaml/Lazy.cmti
lib/ocaml/Lazy.res
lib/ocaml/Lazy.resi
lib/ocaml/Obj.cmi
lib/ocaml/Obj.cmj
lib/ocaml/Obj.cmt
Expand Down Expand Up @@ -1109,6 +1103,12 @@ lib/ocaml/Stdlib_JSON.cmt
lib/ocaml/Stdlib_JSON.cmti
lib/ocaml/Stdlib_JSON.res
lib/ocaml/Stdlib_JSON.resi
lib/ocaml/Stdlib_Lazy.cmi
lib/ocaml/Stdlib_Lazy.cmj
lib/ocaml/Stdlib_Lazy.cmt
lib/ocaml/Stdlib_Lazy.cmti
lib/ocaml/Stdlib_Lazy.res
lib/ocaml/Stdlib_Lazy.resi
lib/ocaml/Stdlib_List.cmi
lib/ocaml/Stdlib_List.cmj
lib/ocaml/Stdlib_List.cmt
Expand Down
41 changes: 0 additions & 41 deletions runtime/Lazy.resi

This file was deleted.

1 change: 1 addition & 0 deletions runtime/Stdlib.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module Float = Stdlib_Float
module Int = Stdlib_Int
module Intl = Stdlib_Intl
module JSON = Stdlib_JSON
module Lazy = Stdlib_Lazy
module List = Stdlib_List
module Math = Stdlib_Math
module Null = Stdlib_Null
Expand Down
10 changes: 6 additions & 4 deletions runtime/Lazy.res → runtime/Stdlib_Lazy.res
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// FIXME:
// This exists for compatibility reason.
// Move this into Pervasives or Core

type t<'a> = lazy_t<'a>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in scope of this PR, but once all "lazy" magic is removed from the compiler (see also #6960), this could just be an abstract type here (instead of a predefined type lazy_t from predef.ml).


exception Undefined = Primitive_lazy.Undefined

let make = Primitive_lazy.from_fun

let get = Primitive_lazy.force

let isEvaluated = Primitive_lazy.is_val

let force = Primitive_lazy.force

let force_val = Primitive_lazy.force_val
Expand Down
111 changes: 111 additions & 0 deletions runtime/Stdlib_Lazy.resi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem necessary to keep deprecated methods here. They were introduced in v12-alpha.1. =)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@namenu they were moved in v12-alpha.1 but they were already in a Lazy module.
example in v11

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/***
This module provides a type `Lazy.t` and functions to create and
manipulate lazy values. A lazy value is a value that is not
computed until it is needed. This is useful for deferring
computations that may be expensive or unnecessary.
*/

/**
The type of a lazy value. `Lazy.t<'a>` represents a lazy value
that will eventually yield a value of type `'a` when accessed.
The value is computed only once, and the result is cached for
subsequent accesses. If the computation raises an exception,
the same exception is raised again on subsequent accesses.
*/
type t<'a> = lazy_t<'a>

/**
`Lazy.make(f)` creates a lazy value from `f` which is the
computation to be deferred of type `unit => 'a`.
The function returns a lazy value of type `Lazy.t<'a>`.
The computation is not executed until the lazy value is accessed.

## Examples
```rescript
let lazyValue = Lazy.make(() => {
// Some expensive computation
Console.log("Computing...")
42
});
lazyValue->Lazy.get->assertEqual(42)
```
*/
let make: (unit => 'a) => t<'a>

/**
`Lazy.get(x)` forces the suspension `x` and returns its result.
If `x` has already been forced, `Lazy.get(x)` returns the
same value again without recomputing it. If it raised an
exception, the same exception is raised again.
Raise `Undefined` if the forcing of `x` tries to force `x` itself
recursively. This is a runtime error.
*/
let get: t<'a> => 'a

/**
`Lazy.isEvaluated(x)` returns `true` if the suspension `x` has
already been forced and did not raise an exception. Otherwise,
it returns `false`. This is useful for checking if a lazy value
has been computed before accessing it.

## Examples
```rescript
let lazyValue = Lazy.make(() => {
// Some expensive computation
Console.log("Computing...")
42
})
Lazy.isEvaluated(lazyValue)->assertEqual(false)
lazyValue->Lazy.get->assertEqual(42)
lazyValue->Lazy.isEvaluated->assertEqual(true)
```
*/
let isEvaluated: t<'a> => bool

exception Undefined

/**
`force(x)` forces the suspension `x` and returns its result.
If `x` has already been forced, `Lazy.force(x)` returns the
same value again without recomputing it. If it raised an exception,
the same exception is raised again.
Raise `Undefined` if the forcing of `x` tries to force `x` itself
recursively.
*/
@deprecated("Use `Lazy.get` instead")
let force: t<'a> => 'a

/**
`force_val(x)` forces the suspension `x` and returns its
result. If `x` has already been forced, `force_val(x)`
returns the same value again without recomputing it.
Raise `Undefined` if the forcing of `x` tries to force `x` itself
recursively.
If the computation of `x` raises an exception, it is unspecified
whether `force_val(x)` raises the same exception or `Undefined`.
*/
@deprecated("Use `Lazy.get` instead")
let force_val: t<'a> => 'a

/**
`Lazy.from_fun(f)` creates a lazy value from `f` which is the
computation to be deferred of type `unit => 'a`.
The function returns a lazy value of type `Lazy.t<'a>`.
The computation is not executed until the lazy value is accessed.
*/
@deprecated("Use `Lazy.make` instead")
let from_fun: (unit => 'a) => t<'a>

/**
`from_val(v)` returns an already-forced suspension of `v`.
This is for special purposes only.
*/
@deprecated("Use `Lazy.make` instead")
let from_val: 'a => t<'a>

/**
`is_val(x)` returns `true` if `x has already been forced and
did not raise an exception.
*/
@deprecated("Use `Lazy.isEvaluated` instead")
let is_val: t<'a> => bool
8 changes: 4 additions & 4 deletions tests/tests/src/gpr_3697_test.mjs
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Lazy from "rescript/lib/es6/Lazy.js";
import * as Stdlib_Lazy from "rescript/lib/es6/Stdlib_Lazy.js";

function fix() {
return {
TAG: "Fix",
_0: Lazy.from_fun(fix)
_0: Stdlib_Lazy.from_fun(fix)
};
Comment on lines +8 to 9
Copy link
Preview

Copilot AI Apr 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stdlib_Lazy.from_fun is used here, but the new API exports the lazy function creation as 'make'. Replace 'Stdlib_Lazy.from_fun' with 'Stdlib_Lazy.make' to avoid potential runtime errors.

Suggested change
_0: Stdlib_Lazy.from_fun(fix)
};
_0: Stdlib_Lazy.make(fix)

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point from Copilot, but this test file is a bit weird anyway, as it doesn't actually run any code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes those are the tests I don't fully understand ^^
from_fun is still valid though and only deprecated for now, do you want me to correct it @cknitt ?

}

function unfixLeak(_f) {
while (true) {
let f = _f;
_f = Lazy.force(f._0);
_f = Stdlib_Lazy.force(f._0);
continue;
};
}

function unfix(p) {
while (true) {
let h = p.contents;
p.contents = Lazy.force(h._0);
p.contents = Stdlib_Lazy.force(h._0);
};
}

Expand Down
Loading