diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4c9841..4b19323 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,8 @@ high state of flux, you're at risk of it changing without notice.
- `Prism`
- (\*) remove `fromSome` constructor (@gcanti)
- (\*) change `fromNullable` signature (@gcanti)
+ - `Optional`
+ - add missing `fromNullable` combinator, closes #133 (@gcanti)
(\*) breaking change
diff --git a/docs/modules/Optional.ts.md b/docs/modules/Optional.ts.md
index ec222fc..62b37c8 100644
--- a/docs/modules/Optional.ts.md
+++ b/docs/modules/Optional.ts.md
@@ -37,6 +37,7 @@ Added in v2.3.0
- [component](#component)
- [filter](#filter)
- [findFirst](#findfirst)
+ - [fromNullable](#fromnullable)
- [index](#index)
- [key](#key)
- [left](#left)
@@ -126,6 +127,18 @@ export declare const findFirst: (predicate: Predicate) => (sa: Optional
Added in v2.3.2
+## fromNullable
+
+Return an `Optional` from a `Optional` focused on a nullable value
+
+**Signature**
+
+```ts
+export declare const fromNullable: (sa: Optional) => Optional>
+```
+
+Added in v2.3.3
+
## index
Return a `Optional` from a `Optional` focused on a `ReadonlyArray`
diff --git a/src/Optional.ts b/src/Optional.ts
index 4b3b9c7..a8b4419 100644
--- a/src/Optional.ts
+++ b/src/Optional.ts
@@ -100,6 +100,16 @@ export const modifyOption: (f: (a: A) => A) => (optional: Optional)
*/
export const modify: (f: (a: A) => A) => (optional: Optional) => (s: S) => S = _.optionalModify
+/**
+ * Return an `Optional` from a `Optional` focused on a nullable value
+ *
+ * @category combinators
+ * @since 2.3.3
+ */
+export const fromNullable: (sa: Optional) => Optional> =
+ /*#__PURE__*/
+ compose(_.prismAsOptional(_.prismFromNullable()))
+
/**
* @category combinators
* @since 2.3.0
diff --git a/src/Prism.ts b/src/Prism.ts
index 0470045..f148884 100644
--- a/src/Prism.ts
+++ b/src/Prism.ts
@@ -120,6 +120,12 @@ export const composeOptional = (ab: Optional) => (sa: Prism
// combinators
// -------------------------------------------------------------------------------------
+/**
+ * @category combinators
+ * @since 2.3.0
+ */
+export const set: (a: A) => (sa: Prism) => (s: S) => S = _.prismSet
+
/**
* @category combinators
* @since 2.3.0
@@ -142,16 +148,6 @@ export const fromNullable: (sa: Prism) => Prism> =
/*#__PURE__*/
compose(_.prismFromNullable())
-/**
- * @category combinators
- * @since 2.3.0
- */
-export const set: (a: A) => (sa: Prism) => (s: S) => S = _.prismSet
-
-// -------------------------------------------------------------------------------------
-// combinators
-// -------------------------------------------------------------------------------------
-
/**
* @category combinators
* @since 2.3.0
diff --git a/test/Optional.ts b/test/Optional.ts
index 28e765f..7ed2552 100644
--- a/test/Optional.ts
+++ b/test/Optional.ts
@@ -142,4 +142,17 @@ describe('Optional', () => {
)
assert.deepStrictEqual(modify(O.some({ a: ['a'] })), O.some({ a: ['A'] }))
})
+
+ it('fromNullable', () => {
+ interface S {
+ a?: number
+ }
+ const sa = pipe(_.id(), _.prop('a'), _.fromNullable)
+ assert.deepStrictEqual(sa.getOption({}), O.none)
+ assert.deepStrictEqual(sa.getOption({ a: undefined }), O.none)
+ assert.deepStrictEqual(sa.getOption({ a: 1 }), O.some(1))
+ assert.deepStrictEqual(sa.set(2)({}), {})
+ assert.deepStrictEqual(sa.set(2)({ a: undefined }), { a: undefined })
+ assert.deepStrictEqual(sa.set(2)({ a: 1 }), { a: 2 })
+ })
})