Exploring Option Conversions in Effect-TS
Tags: javascript, typescript, effect, functional
Published: 2024-07-16 18:05:41

Effect-TS provides powerful tools for handling `Option` and `Either` types. In this article, we'll explore various ways to convert and manipulate these types using the library's utility functions.

## Example 1: Convert an Either to an Option with `O.getRight`

The `O.getRight` function converts an `Either` to an `Option`, discarding the error. If the `Either` is `Right`, it returns `O.some(value)`, otherwise it returns `O.none`.

```typescript
import { Option as O, Either as E, pipe } from 'effect';

function conversions_ex01() {
  const eitherRight = E.right('ok'); // Create an Either containing the value 'ok'
  const eitherLeft = E.left('error'); // Create an Either representing an error

  console.log(O.getRight(eitherRight)); // Output: Some('ok')
  console.log(O.getRight(eitherLeft)); // Output: None
}
```

## Example 2: Convert an Either to an Option with `O.getLeft`

The `O.getLeft` function converts an `Either` to an `Option`, discarding the value. If the `Either` is `Left`, it returns `O.some(error)`, otherwise it returns `O.none`.

```typescript
import { Option as O, Either as E, pipe } from 'effect';

function conversions_ex02() {
  const eitherRight = E.right('ok'); // Create an Either containing the value 'ok'
  const eitherLeft = E.left('error'); // Create an Either representing an error

  console.log(O.getLeft(eitherRight)); // Output: None
  console.log(O.getLeft(eitherLeft)); // Output: Some('error')
}
```

## Example 3: Get Value or Default with `O.getOrElse`

The `O.getOrElse` function returns the value inside the `Option` if it is `Some`, otherwise, it returns the provided default value.

```typescript
import { Option as O, pipe } from 'effect';

function conversions_ex03() {
  const some = O.some(1); // Create an Option containing the value 1
  const none = O.none(); // Create an Option representing no value

  console.log(
    pipe(
      some,
      O.getOrElse(() => 'default')
    )
  ); // Output: 1 (since some contains 1)
  console.log(
    pipe(
      none,
      O.getOrElse(() => 'default')
    )
  ); // Output: 'default' (since none is None)
}
```

## Example 4: Chaining Options with `O.orElse`

The `O.orElse` function returns the provided Option `that` if `self` is `None`, otherwise it returns `self`. This function allows chaining of Options where the fallback is another Option.

```typescript
import { Option as O, pipe } from 'effect';

function conversions_ex04() {
  const some1 = O.some(1); // Create an Option containing the value 1
  const some2 = O.some(2); // Create an Option containing the value 2
  const none = O.none(); // Create an Option representing no value

  console.log(
    pipe(
      some1,
      O.orElse(() => some2)
    )
  ); // Output: Some(1) (since some1 contains 1)
  console.log(
    pipe(
      none,
      O.orElse(() => some2)
    )
  ); // Output: Some(2) (since none is None and fallback is some2)
}
```

## Example 5: Fallback to a Default Value with `O.orElseSome`

The `O.orElseSome` function returns the provided default value wrapped in `Some` if `self` is `None`, otherwise it returns `self`. This function allows chaining of Options where the fallback is a default value wrapped in `Some`.

```typescript
import { Option as O, pipe } from 'effect';

function conversions_ex05() {
  const some = O.some(1); // Create an Option containing the value 1
  const none = O.none(); // Create an Option representing no value

  console.log(
    pipe(
      some,
      O.orElseSome(() => 2)
    )
  ); // Output: Some(1) (since some contains 1)
  console.log(
    pipe(
      none,
      O.orElseSome(() => 2)
    )
  ); // Output: Some(2) (since none is None and fallback is 2)
}
```

## Example 6: Chaining Options with Either Context using `O.orElseEither`

The `O.orElseEither` function returns an Option containing an Either where `Left` is from the fallback Option and `Right` is from the original Option. This function allows chaining of Options where the fallback provides an Either for more context.

```typescript
import { Option as O, Either as E, pipe } from 'effect';

function conversions_ex06() {
  const some1 = O.some(1); // Create an Option containing the value 1
  const some2 = O.some(2); // Create an Option containing the value 2
  const none = O.none(); // Create an Option representing no value

  console.log(
    pipe(
      some1,
      O.orElseEither(() => some2)
    )
  ); // Output: Some(Right(1)) (since some1 contains 1)
  console.log(
    pipe(
      none,
      O.orElseEither(() => some2)
    )
  ); // Output: Some(Left(2)) (since none is None and fallback is some2)
}
```

## Example 7: Find the First `Some` in an Iterable with `O.firstSomeOf`

The `O.firstSomeOf` function returns the first `Some` found in an iterable of Options. If all are `None`, it returns `None`.

```typescript
import { Option as O } from 'effect';

function conversions_ex07() {
  const options = [O.none(), O.some(1), O.some(2)]; // Create an iterable of Options
  const optionsAllNone = [O.none(), O.none()]; // Create an iterable of None Options

  console.log(O.firstSomeOf(options)); // Output: Some(1) (since the first non-None Option is Some(1))
  console.log(O.firstSomeOf(optionsAllNone)); // Output: None (since all Options are None)
}
```

## Example 8: Convert a Function Returning an Option to a Type Guard with `O.toRefinement`

The `O.toRefinement` function converts a function returning an Option to a type guard, allowing more specific type checking.

```typescript
import { Option as O } from 'effect';

function conversions_ex08() {
  const isPositive = (n: number): O.Option<number> => n > 0 ? O.some(n) : O.none();
  const isPositiveRefinement = O.toRefinement(isPositive);

  console.log(isPositiveRefinement(1)); // Output: true (since 1 is positive)
  console.log(isPositiveRefinement(-1)); // Output: false (since -1 is not positive)
}
```

## Example 9: Convert an Option to an Array with `O.toArray`

The `O.toArray` function converts an Option to an array. If the Option is `Some`, it returns an array containing the value; if it is `None`, it returns an empty array.

```typescript
import { Option as O } from 'effect';

function conversions_ex09() {
  const some = O.some(1); // Create an Option containing the value 1
  const none = O.none(); // Create an Option representing no value

  console.log(O.toArray(some)); // Output: [1] (since some contains 1)
  console.log(O.toArray(none)); // Output: [] (since none is None)
}
```

## Conclusion

In this article, we've explored various functions provided by Effect-TS for converting and manipulating `Option` and `Either` types. These functions enhance the flexibility and expressiveness of your code, allowing you to handle optional and error-prone values more gracefully. Whether you need to convert an `Either` to an `Option`, chain multiple `Option` values, or perform type-safe operations, Effect-TS offers a robust set of tools to simplify these tasks. By leveraging these utilities, you can write cleaner, more maintainable code that effectively handles the presence or absence of values.