Skip to content

Commit

Permalink
feat(option): Add productAll method to Option class
Browse files Browse the repository at this point in the history
The 'productAll' method has been added to the Option class. This
method takes a structure of Options and turns it into an Option of
values with the same structure. The 'Product' type has also been
imported from the typeclass module to support the implementation.
  • Loading branch information
suddenlyGiovanni committed Jun 23, 2024
1 parent 81322c3 commit 8506525
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 7 deletions.
16 changes: 16 additions & 0 deletions src/option/option.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,22 @@ describe('Option', () => {
})
})

describe('Product', () => {
test('Some', () => {
Util.optionEqual(
Option.productAll([Option.Some(1), Option.Some(2), Option.Some(3)]),
Option.Some([1, 2, 3]),
)
})

test('None', () => {
Util.optionEqual(
Option.productAll([Option.Some(1), Option.None(), Option.Some(3)]),
Option.None(),
)
})
})

describe('Monad', () => {
test('of', () => {
expect(Option.isOption(Option.of(1))).toBe(true)
Expand Down
51 changes: 50 additions & 1 deletion src/option/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type Foldable,
type Monad,
type Pointed,
type Product,
type SemiProduct,
} from '../typeclass/mod.ts'

Expand Down Expand Up @@ -575,6 +576,53 @@ export abstract class Option<out A>
? Option.of([self.get(), that.get()])
: Option.None()

/**
* Takes a structure of Options and returns an Option of values with the same structure.
*
* @typeParam A - The type of the values in the Options.
* @param optionCollection - The structure of Options to combine.
* @returns An Option of values with the same structure.
*
* @remarks
* It implements the {@linkcode Product#Pipeable#productAll} type class interface.
*
* @example
* All Some
* ```ts
* import { assertEquals } from 'jsr:@std/assert'
* import { Option } from './option.ts'
*
* assertEquals(
* Option.productAll([Option.Some(1), Option.Some(2), Option.Some(3)]),
* Option.Some([1, 2, 3])
* )
* ```
*
* @example
* Some and None
* ```ts
* import { assertEquals } from 'jsr:@std/assert'
* import { Option } from './option.ts'
*
* assertEquals(
* Option.productAll([Option.Some(1), Option.None(), Option.Some(3)]),
* Option.None()
* )
* ```
*/
public static readonly productAll: Product.Pipeable<Option.TypeLambda>['productAll'] = <A>(
optionCollection: Iterable<Option.Type<A>>,
): Option.Type<Array<A>> => {
const out: A[] = []
for (const option of optionCollection) {
if (Option.isNone(option)) {
return Option.None()
}
out.push(option.get())
}
return Option.of(out)
}

/**
* Combines an `Option<A>` from 'self' and an iterable collection of `Option<A>` into an `Option<[A, ...Array<A>]>`.
* If any of the options are `None`, the result will be `None`.
Expand Down Expand Up @@ -1173,7 +1221,8 @@ function assertPipableOption<
& FlatMap.Pipeable<Option.TypeLambda>
& Pointed.Pipeable<Option.TypeLambda>
& Monad.Pipeable<Option.TypeLambda>
& SemiProduct.Pipeable<Option.TypeLambda>,
& SemiProduct.Pipeable<Option.TypeLambda>
& Product.Pipeable<Option.TypeLambda>,
>(_option: T): void {
return
}
Expand Down
12 changes: 6 additions & 6 deletions src/typeclass/of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type { Kind, TypeClass, TypeLambda } from '../internal/hkt.ts'
/**
* The `Of` type class is used to lifts any value into a context.
*
* | Name | Given | To |
* | | | |
* | **of** | `A` | `F<A>` |
* | ~ofComposition~ | ~`A`~ | ~`F<G<A>>`~ |
* | ~unit~ | | ~`F<void>`~ |
* | ~Do~ | | ~`F<{}>`~ |
* | Name | Given | To |
* |-----------------|-------|-------------|
* | **of** | `A` | `F<A>` |
* | ~ofComposition~ | ~`A`~ | ~`F<G<A>>`~ |
* | ~unit~ | | ~`F<void>`~ |
* | ~Do~ | | ~`F<{}>`~ |
*
* @module
* @category typeclass
Expand Down

0 comments on commit 8506525

Please sign in to comment.