From 0a82ffa562bfedcf041ad8ad27694aacdc6af446 Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 20:32:23 +0100 Subject: [PATCH 01/10] feat(nullable): add getExn --- src/Core__Nullable.mjs | 12 ++++++++++++ src/Core__Nullable.res | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/Core__Nullable.mjs b/src/Core__Nullable.mjs index bdc0e58c..58165807 100644 --- a/src/Core__Nullable.mjs +++ b/src/Core__Nullable.mjs @@ -9,7 +9,19 @@ function fromOption(option) { } +function getExn(value) { + if (!(value == null)) { + return value; + } + throw { + RE_EXN_ID: "Invalid_argument", + _1: "Nullable.getExn: value is null or undefined", + Error: new Error() + }; +} + export { fromOption , + getExn , } /* No side effect */ diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index 044a037b..abc66719 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -13,3 +13,9 @@ let fromOption: option<'a> => t<'a> = option => | Some(x) => make(x) | None => undefined } + +let getExn: t<'a> => 'a = value => + switch value->toOption { + | Some(x) => x + | None => raise(Invalid_argument("Nullable.getExn: value is null or undefined")) + } From 5fb7c074405348fd1be5e5cdd0af94ccb6ab8f1c Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 20:32:39 +0100 Subject: [PATCH 02/10] feat(nullable): add getUnsafe --- src/Core__Nullable.res | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index abc66719..6add5c4d 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -19,3 +19,5 @@ let getExn: t<'a> => 'a = value => | Some(x) => x | None => raise(Invalid_argument("Nullable.getExn: value is null or undefined")) } + +external getUnsafe: t<'a> => 'a = "%identity" From 2414c377dead9db28b6fe53abe810c5c4f36b85a Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 20:43:51 +0100 Subject: [PATCH 03/10] feat(nullable): add getWithDefault --- src/Core__Nullable.mjs | 9 +++++++++ src/Core__Nullable.res | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/Core__Nullable.mjs b/src/Core__Nullable.mjs index 58165807..40d5409e 100644 --- a/src/Core__Nullable.mjs +++ b/src/Core__Nullable.mjs @@ -9,6 +9,14 @@ function fromOption(option) { } +function getWithDefault(value, $$default) { + if (value == null) { + return $$default; + } else { + return value; + } +} + function getExn(value) { if (!(value == null)) { return value; @@ -22,6 +30,7 @@ function getExn(value) { export { fromOption , + getWithDefault , getExn , } /* No side effect */ diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index 6add5c4d..b40f6b59 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -14,6 +14,12 @@ let fromOption: option<'a> => t<'a> = option => | None => undefined } +let getWithDefault = (value, default) => + switch value->toOption { + | Some(x) => x + | None => default + } + let getExn: t<'a> => 'a = value => switch value->toOption { | Some(x) => x From f783ea0c01f5a712cad32ec0a002ebf3f2c142f1 Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 20:44:17 +0100 Subject: [PATCH 04/10] feat(nullable): add map --- src/Core__Nullable.mjs | 9 +++++++++ src/Core__Nullable.res | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/Core__Nullable.mjs b/src/Core__Nullable.mjs index 40d5409e..1e128b36 100644 --- a/src/Core__Nullable.mjs +++ b/src/Core__Nullable.mjs @@ -1,5 +1,6 @@ // Generated by ReScript, PLEASE EDIT WITH CARE +import * as Curry from "rescript/lib/es6/curry.js"; import * as Caml_option from "rescript/lib/es6/caml_option.js"; function fromOption(option) { @@ -28,9 +29,17 @@ function getExn(value) { }; } +function map(value, f) { + if (!(value == null)) { + return Curry._1(f, value); + } + +} + export { fromOption , getWithDefault , getExn , + map , } /* No side effect */ diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index b40f6b59..e4044713 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -27,3 +27,9 @@ let getExn: t<'a> => 'a = value => } external getUnsafe: t<'a> => 'a = "%identity" + +let map = (value, f) => + switch value->toOption { + | Some(x) => make(f(x)) + | None => undefined + } From ea9b7c0898c7c1f32865320d2deb55f74c694c5d Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 20:44:57 +0100 Subject: [PATCH 05/10] feat(nullable): add mapWithDefault --- src/Core__Nullable.mjs | 9 +++++++++ src/Core__Nullable.res | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/Core__Nullable.mjs b/src/Core__Nullable.mjs index 1e128b36..7ac059a5 100644 --- a/src/Core__Nullable.mjs +++ b/src/Core__Nullable.mjs @@ -36,10 +36,19 @@ function map(value, f) { } +function mapWithDefault(value, $$default, f) { + if (value == null) { + return $$default; + } else { + return Curry._1(f, value); + } +} + export { fromOption , getWithDefault , getExn , map , + mapWithDefault , } /* No side effect */ diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index e4044713..e31b2ac1 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -33,3 +33,9 @@ let map = (value, f) => | Some(x) => make(f(x)) | None => undefined } + +let mapWithDefault = (value, default, f) => + switch value->toOption { + | Some(x) => f(x) + | None => default + } From 1816067592d3623ccb968fc8a18d29c6efaeca82 Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 20:45:11 +0100 Subject: [PATCH 06/10] feat(nullable): add flatMap --- src/Core__Nullable.mjs | 8 ++++++++ src/Core__Nullable.res | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/Core__Nullable.mjs b/src/Core__Nullable.mjs index 7ac059a5..a9c04618 100644 --- a/src/Core__Nullable.mjs +++ b/src/Core__Nullable.mjs @@ -44,11 +44,19 @@ function mapWithDefault(value, $$default, f) { } } +function flatMap(value, f) { + if (!(value == null)) { + return Curry._1(f, value); + } + +} + export { fromOption , getWithDefault , getExn , map , mapWithDefault , + flatMap , } /* No side effect */ diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index e31b2ac1..e7c40cca 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -39,3 +39,9 @@ let mapWithDefault = (value, default, f) => | Some(x) => f(x) | None => default } + +let flatMap = (value, f) => + switch value->toOption { + | Some(x) => f(x) + | None => undefined + } From aed4681f57896c83fd1d6b50ec11f2c48e648ecc Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 22:14:39 +0100 Subject: [PATCH 07/10] docs(nullable): add interface --- src/Core__Nullable.resi | 96 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/Core__Nullable.resi b/src/Core__Nullable.resi index 41345eb4..0e49a5ee 100644 --- a/src/Core__Nullable.resi +++ b/src/Core__Nullable.resi @@ -81,3 +81,99 @@ let asNullable = optString->Nullable.fromOption // Nullable.t ``` */ let fromOption: option<'a> => t<'a> + +/** +`getWithDefault(value, default)` returns `value` if not `null` or `undefined`, +otherwise return `default`. + +## Examples + +```rescript +Nullable.getWithDefault(Nullable.null, "Banana") // Banana +Nullable.getWithDefault(Nulalble.make("Apple"), "Banana") // Apple + +let greet = (firstName: option) => + "Greetings " ++ firstName->Nullable.getWithDefault("Anonymous") + +Nullable.make("Jane")->greet // "Greetings Jane" +Nullable.null->greet // "Greetings Anonymous" +``` +*/ +let getWithDefault: (t<'a>, 'a) => 'a + +/** +`getExn(value)` raises an exception if `null` or `undefined`, otherwise returns the value. + +```rescript +Nullable.getExn(Nullable.make(3)) // 3 +Nullable.getExn(Nullable.null) /* Raises an Error */ +``` + +## Exceptions + +- Raises `Invalid_argument` if `value` is `null` or `undefined` +*/ +let getExn: t<'a> => 'a + +/** +`getUnsafe(value)` returns `value`. + +## Examples + +```rescript +Nullable.getUnsafe(Nullable.make(3)) == 3 +Nullable.getUnsafe(Nullable.null) // Raises an error +``` + +## Important + +- This is an unsafe operation, it assumes `value` is not `null` or `undefined`. +*/ +external getUnsafe: t<'a> => 'a = "%identity" + +/** +`map(value, f)` returns `f(value)` if `value` is not `null` or `undefined`, otherwise `undefined`. + +## Examples + +```rescript +Nullable.map(Nullable.make(3), x => x * x) // Nullable.make(9) +Nullable.map(Nullable.undefined, x => x * x) // Nullable.undefined +``` +*/ +let map: (t<'a>, 'a => 'b) => t<'b> + +/** +`mapWithDefault(value, default, f)` returns `f(value)` if `value` is not `null` or `undefined`, otherwise `default`. + +## Examples + +```rescript +let someValue = Nullable.make(3) +someValue->Nullable.mapWithDefault(0, x => x + 5) // 8 + +let noneValue = Nullable.null +noneValue->Nullable.mapWithDefault(0, x => x + 5) // 0 +``` +*/ +let mapWithDefault: (t<'a>, 'b, 'a => 'b) => 'b + +/** +`flatMap(value, f)` returns `f(x)` if `value` is not `null` or `undefined`, otherwise `undefined`. + +## Examples + +```rescript +let addIfAboveOne = value => + if (value > 1) { + Nullable.make(value + 1) + } else { + Nullable.null + } + +Nullable.flatMap(Nullable.make(2), addIfAboveOne) // Nullable.make(3) +Nullable.flatMap(Nullable.make(-4), addIfAboveOne) // Nullable.undefined +Nullable.flatMap(Nullable.null, addIfAboveOne) // Nullable.undefined +``` +*/ +let flatMap: (t<'a>, 'a => t<'b>) => t<'b> From d94cc445c6d0b8380f9835c00aa16d48f40b3f90 Mon Sep 17 00:00:00 2001 From: glennsl Date: Thu, 23 Feb 2023 22:21:08 +0100 Subject: [PATCH 08/10] docs(changelog): add changelog entry for Nullable additions --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99fe1e50..8b5fe554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Remove `Date.valueOf` as it returns the same as `Date.getTime`. https://github.com/rescript-association/rescript-core/pull/61 - Change `Float.fromString` signature. Now accepts only string. https://github.com/rescript-association/rescript-core/pull/54 - Change `Float.parseFloat` signature. Now accepts only string. https://github.com/rescript-association/rescript-core/pull/54 +- Add `getExn`, `getUnsafe`, `getWithDefault`, `map`, `mapWithDefault` and `flatMap` to `Nullable`. https://github.com/rescript-association/rescript-core/pull/67 ### Documentation From 568ad0c0e6664fd6fabc46499f096bce95abdc62 Mon Sep 17 00:00:00 2001 From: Glenn Slotte Date: Thu, 23 Feb 2023 22:37:24 +0100 Subject: [PATCH 09/10] docs(nullable): fix typo x -> value Co-authored-by: Pedro Castro --- src/Core__Nullable.resi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core__Nullable.resi b/src/Core__Nullable.resi index 0e49a5ee..55cbbf45 100644 --- a/src/Core__Nullable.resi +++ b/src/Core__Nullable.resi @@ -159,7 +159,7 @@ noneValue->Nullable.mapWithDefault(0, x => x + 5) // 0 let mapWithDefault: (t<'a>, 'b, 'a => 'b) => 'b /** -`flatMap(value, f)` returns `f(x)` if `value` is not `null` or `undefined`, otherwise `undefined`. +`flatMap(value, f)` returns `f(value)` if `value` is not `null` or `undefined`, otherwise `undefined`. ## Examples From 3136da6f20083e4372bd00d45babaac65ccd8605 Mon Sep 17 00:00:00 2001 From: glennsl Date: Fri, 24 Feb 2023 09:37:59 +0100 Subject: [PATCH 10/10] feat(nullable/map+flatMap): return value unchanged if null or undefined --- src/Core__Nullable.mjs | 10 ++++++---- src/Core__Nullable.res | 4 ++-- src/Core__Nullable.resi | 9 ++++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Core__Nullable.mjs b/src/Core__Nullable.mjs index a9c04618..1a7b1ff3 100644 --- a/src/Core__Nullable.mjs +++ b/src/Core__Nullable.mjs @@ -30,10 +30,11 @@ function getExn(value) { } function map(value, f) { - if (!(value == null)) { + if (value == null) { + return value; + } else { return Curry._1(f, value); } - } function mapWithDefault(value, $$default, f) { @@ -45,10 +46,11 @@ function mapWithDefault(value, $$default, f) { } function flatMap(value, f) { - if (!(value == null)) { + if (value == null) { + return value; + } else { return Curry._1(f, value); } - } export { diff --git a/src/Core__Nullable.res b/src/Core__Nullable.res index e7c40cca..0d74399a 100644 --- a/src/Core__Nullable.res +++ b/src/Core__Nullable.res @@ -31,7 +31,7 @@ external getUnsafe: t<'a> => 'a = "%identity" let map = (value, f) => switch value->toOption { | Some(x) => make(f(x)) - | None => undefined + | None => Obj.magic(value) } let mapWithDefault = (value, default, f) => @@ -43,5 +43,5 @@ let mapWithDefault = (value, default, f) => let flatMap = (value, f) => switch value->toOption { | Some(x) => f(x) - | None => undefined + | None => Obj.magic(value) } diff --git a/src/Core__Nullable.resi b/src/Core__Nullable.resi index 55cbbf45..8037cdac 100644 --- a/src/Core__Nullable.resi +++ b/src/Core__Nullable.resi @@ -132,7 +132,8 @@ Nullable.getUnsafe(Nullable.null) // Raises an error external getUnsafe: t<'a> => 'a = "%identity" /** -`map(value, f)` returns `f(value)` if `value` is not `null` or `undefined`, otherwise `undefined`. +`map(value, f)` returns `f(value)` if `value` is not `null` or `undefined`, +otherwise returns `value` unchanged. ## Examples @@ -144,7 +145,8 @@ Nullable.map(Nullable.undefined, x => x * x) // Nullable.undefined let map: (t<'a>, 'a => 'b) => t<'b> /** -`mapWithDefault(value, default, f)` returns `f(value)` if `value` is not `null` or `undefined`, otherwise `default`. +`mapWithDefault(value, default, f)` returns `f(value)` if `value` is not `null` +or `undefined`, otherwise returns `default`. ## Examples @@ -159,7 +161,8 @@ noneValue->Nullable.mapWithDefault(0, x => x + 5) // 0 let mapWithDefault: (t<'a>, 'b, 'a => 'b) => 'b /** -`flatMap(value, f)` returns `f(value)` if `value` is not `null` or `undefined`, otherwise `undefined`. +`flatMap(value, f)` returns `f(value)` if `value` is not `null` or `undefined`, +otherwise returns `value` unchanged. ## Examples