Skip to content
Open
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 @@ -19,6 +19,7 @@
#### :rocket: New Feature

- Add source map support with linked, inline, and hidden modes. https://github.com/rescript-lang/rescript/pull/8393
- Add `List.includes`, deprecate `List.has` in favor of `List.some`, and clarify the equality semantics of `List.includes` and `Array.includes`. https://github.com/rescript-lang/rescript/pull/8530

#### :bug: Bug fix

Expand Down
6 changes: 4 additions & 2 deletions packages/@rescript/runtime/Stdlib_Array.resi
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,8 @@ See [`Array.flat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
external flat: array<array<'a>> => array<'a> = "flat"

/**
`includes(array, item)` checks whether `array` includes `item`, by doing a [strict check for equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality).
`includes(array, item)` checks whether `array` includes `item` using
[SameValueZero equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness#same-value-zero_equality).
Comment thread
cknitt marked this conversation as resolved.

See [`Array.includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) on MDN.

Expand All @@ -618,8 +619,9 @@ See [`Array.includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/R
```rescript
[1, 2]->Array.includes(1) == true
[1, 2]->Array.includes(3) == false
[Float.Constants.nan]->Array.includes(Float.Constants.nan) == true

[{"language": "ReScript"}]->Array.includes({"language": "ReScript"}) == false // false, because of strict equality
[{"language": "ReScript"}]->Array.includes({"language": "ReScript"}) == false // false, because objects compare by reference
```
*/
@send
Expand Down
6 changes: 6 additions & 0 deletions packages/@rescript/runtime/Stdlib_List.res
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,12 @@ let rec has = (xs, x, eq) =>
| list{a, ...l} => eq(a, x) || has(l, x, eq)
}

let rec includes = (xs, x) =>
switch xs {
| list{} => false
| list{a, ...l} => a === x || includes(l, x)
}

@deprecated("Use a `Map` instead")
let rec getAssoc = (xs, x, eq) =>
switch xs {
Expand Down
23 changes: 23 additions & 0 deletions packages/@rescript/runtime/Stdlib_List.resi
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,8 @@ let equal: (list<'a>, list<'a>, ('a, 'a) => bool) => bool
`has(list, element, f)` returns `true` if the list contains at least one
`element` for which `f` returns \`true'.

Use [`some`](#some) with a predicate instead.

## Examples

```rescript
Expand All @@ -769,8 +771,29 @@ list{1, 2, 3}->List.has(4, (a, b) => a == b) == false
list{-1, -2, -3}->List.has(2, (a, b) => abs(a) == abs(b)) == true
```
*/
@deprecated("Use `some` instead")
let has: (list<'a>, 'b, ('a, 'b) => bool) => bool

/**
`includes(list, item)` checks whether `list` includes `item` using strict equality.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Which is different from Array.includes which uses SameValueZero equality, but I was unsure if the NaN edge case is worth complicating the implementation.


Object values are only equal when they refer to the same object. `NaN` is not
equal to itself.

## Examples

```rescript
list{1, 2}->List.includes(1) == true
list{1, 2}->List.includes(3) == false
list{Float.Constants.nan}->List.includes(Float.Constants.nan) == false

let item = {"language": "ReScript"}
list{item}->List.includes(item) == true
list{item}->List.includes({"language": "ReScript"}) == false
```
*/
let includes: (list<'a>, 'a) => bool
Comment thread
cknitt marked this conversation as resolved.

/**
`find(list, f)` returns `Some(value)` for the first value in `list` that
satisfies the predicate function `f`. Returns `None` if no element satisfies
Expand Down
15 changes: 15 additions & 0 deletions packages/@rescript/runtime/lib/es6/Stdlib_List.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,20 @@ function has(_xs, x, eq) {
};
}

function includes(_xs, x) {
while (true) {
let xs = _xs;
if (xs === 0) {
return false;
}
if (xs.hd === x) {
return true;
}
_xs = xs.tl;
continue;
};
}

function getAssoc(_xs, x, eq) {
while (true) {
let xs = _xs;
Expand Down Expand Up @@ -1351,6 +1365,7 @@ export {
compare,
equal,
has,
includes,
find,
filter,
filterWithIndex,
Expand Down
15 changes: 15 additions & 0 deletions packages/@rescript/runtime/lib/js/Stdlib_List.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,20 @@ function has(_xs, x, eq) {
};
}

function includes(_xs, x) {
while (true) {
let xs = _xs;
if (xs === 0) {
return false;
}
if (xs.hd === x) {
return true;
}
_xs = xs.tl;
continue;
};
}

function getAssoc(_xs, x, eq) {
while (true) {
let xs = _xs;
Expand Down Expand Up @@ -1350,6 +1364,7 @@ exports.compareLength = compareLength;
exports.compare = compare;
exports.equal = equal;
exports.has = has;
exports.includes = includes;
exports.find = find;
exports.filter = filter;
exports.filterWithIndex = filterWithIndex;
Expand Down
2 changes: 1 addition & 1 deletion tests/analysis_tests/tests/src/expected/Completion.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ Path Array.
"detail": "(array<'a>, 'a) => bool",
"documentation": {
"kind": "markdown",
"value": "\n`includes(array, item)` checks whether `array` includes `item`, by doing a [strict check for equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality).\n\nSee [`Array.includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) on MDN.\n\n## Examples\n\n```rescript\n[1, 2]->Array.includes(1) == true\n[1, 2]->Array.includes(3) == false\n\n[{\"language\": \"ReScript\"}]->Array.includes({\"language\": \"ReScript\"}) == false // false, because of strict equality\n```\n"
"value": "\n`includes(array, item)` checks whether `array` includes `item` using\n[SameValueZero equality](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness#same-value-zero_equality).\n\nSee [`Array.includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) on MDN.\n\n## Examples\n\n```rescript\n[1, 2]->Array.includes(1) == true\n[1, 2]->Array.includes(3) == false\n[Float.Constants.nan]->Array.includes(Float.Constants.nan) == true\n\n[{\"language\": \"ReScript\"}]->Array.includes({\"language\": \"ReScript\"}) == false // false, because objects compare by reference\n```\n"
},
"kind": 12,
"label": "includes",
Expand Down
91 changes: 91 additions & 0 deletions tests/tests/src/stdlib/Stdlib_ListTests.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Test from "./Test.mjs";
import * as Stdlib_List from "@rescript/runtime/lib/es6/Stdlib_List.mjs";
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.mjs";

let eq = Primitive_object.equal;

Test.run([
[
"Stdlib_ListTests.res",
3,
20,
40
],
"includes - present"
], Stdlib_List.includes({
hd: 1,
tl: {
hd: 2,
tl: {
hd: 3,
tl: /* [] */0
}
}
}, 2), eq, true);

Test.run([
[
"Stdlib_ListTests.res",
4,
20,
40
],
"includes - missing"
], Stdlib_List.includes({
hd: 1,
tl: {
hd: 2,
tl: {
hd: 3,
tl: /* [] */0
}
}
}, 4), eq, false);

Test.run([
[
"Stdlib_ListTests.res",
5,
20,
38
],
"includes - empty"
], Stdlib_List.includes(/* [] */0, 1), eq, false);

let item = {
language: "ReScript"
};

let items = {
hd: item,
tl: /* [] */0
};

Test.run([
[
"Stdlib_ListTests.res",
11,
22,
46
],
"includes - same object"
], Stdlib_List.includes(items, item), eq, true);

Test.run([
[
"Stdlib_ListTests.res",
13,
15,
53
],
"includes - structurally equal object"
], Stdlib_List.includes(items, {
language: "ReScript"
}), eq, false);

export {
eq,
}
/* Not a pure module */
18 changes: 18 additions & 0 deletions tests/tests/src/stdlib/Stdlib_ListTests.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
let eq = (a, b) => a == b

Test.run(__POS_OF__("includes - present"), list{1, 2, 3}->List.includes(2), eq, true)
Test.run(__POS_OF__("includes - missing"), list{1, 2, 3}->List.includes(4), eq, false)
Test.run(__POS_OF__("includes - empty"), list{}->List.includes(1), eq, false)

{
let item = {"language": "ReScript"}
let items = list{item}

Test.run(__POS_OF__("includes - same object"), items->List.includes(item), eq, true)
Test.run(
__POS_OF__("includes - structurally equal object"),
items->List.includes({"language": "ReScript"}),
eq,
false,
)
}
1 change: 1 addition & 0 deletions tests/tests/src/stdlib/Stdlib_TestSuite.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as Stdlib_IntTests from "./Stdlib_IntTests.mjs";
import * as Stdlib_DictTests from "./Stdlib_DictTests.mjs";
import * as Stdlib_JsonTests from "./Stdlib_JsonTests.mjs";
import * as Stdlib_ListTests from "./Stdlib_ListTests.mjs";
import * as Stdlib_TestTests from "./Stdlib_TestTests.mjs";
import * as Stdlib_ArrayTests from "./Stdlib_ArrayTests.mjs";
import * as Stdlib_ErrorTests from "./Stdlib_ErrorTests.mjs";
Expand Down
1 change: 1 addition & 0 deletions tests/tests/src/stdlib/Stdlib_TestSuite.res
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ include Stdlib_TestTests
include Stdlib_PromiseTest
include Stdlib_ErrorTests
include Stdlib_ArrayTests
include Stdlib_ListTests
include Stdlib_IntTests
include Stdlib_ObjectTests
include Stdlib_ResultTests
Expand Down
Loading