Skip to content
Merged
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
254 changes: 191 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ This is a set of implementations of monads in TypeScript with OOP perspective.
* [Mapping over an Either](#mapping-over-an-either)
* [Using `flatMap` and `flatMapLeft`](#using-flatmap-and-flatmapleft)
* [Using `map` and `mapLeft`](#using-map-and-mapleft)
* [Matching an Either](#matching-an-either)
* [Recovering from a Left value](#recovering-from-a-left-value)
* [Running side effects](#running-side-effects)
* [Folding an Either](#folding-an-either)
* [checking if an Either is Right or Left](#checking-if-an-either-is-right-or-left)
* [Chaining operations](#chaining-operations)
* [Handling errors](#handling-errors)
Expand All @@ -24,15 +26,18 @@ This is a set of implementations of monads in TypeScript with OOP perspective.
* [Mapping over an Option](#mapping-over-an-option)
* [Using `flatMap`](#using-flatmap)
* [Using `map`](#using-map)
* [Matching an Option](#matching-an-option)
* [Running side effects](#running-side-effects-1)
* [Folding an Option](#folding-an-option)
* [Checking if an Option is Some or None](#checking-if-an-option-is-some-or-none)
* [Try Monad](#try-monad)
* [Usage](#usage-2)
* [Using `map`](#using-map-1)
* [Using `flatMap`](#using-flatmap-1)
* [Matching a Try](#matching-a-try)
* [Handling errors in Infrastructure code](#handling-errors-in-infrastructure-code)
* [Checking if a Try is Success or Failure](#checking-if-a-try-is-success-or-failure)
* [Using `map`](#using-map-1)
* [Using `flatMap`](#using-flatmap-1)
* [Running side effects](#running-side-effects-2)
* [Retrieving the value](#retrieving-the-value)
* [Folding a Try](#folding-a-try)
* [Handling errors in Infrastructure code](#handling-errors-in-infrastructure-code)
* [Checking if a Try is Success or Failure](#checking-if-a-try-is-success-or-failure)
* [Future Monad](#future-monad)
* [Usage](#usage-3)
* [Creating a Future](#creating-a-future)
Expand Down Expand Up @@ -105,7 +110,9 @@ transform the value inside a `Left`.
##### Using `flatMap` and `flatMapLeft`

```typescript
import { Either } from '@leanmind/monads';m
import { Either } from '@leanmind/monads';

m

const right = Either.right(42).flatMap(x => Either.right(x + 1)); // Right(43)
const left = Either.left('Error').flatMapLeft(err => Either.left(`New ${err}`)); // Left('New Error')
Expand All @@ -120,22 +127,62 @@ const right = Either.right(42).map(x => x + 1); // Right(43)
const left = Either.left('Error').mapLeft(err => `New ${err}`); // Left('New Error')
```

#### Matching an Either
#### Recovering from a Left value

You can use the `recover` method to recover from a `Left` value and transform it into a `Right`.

```typescript
import { Either } from '@leanmind/monads';

const recoverIfEven = (x: number) => {
if (x % 2 === 0) {
return Either.right('Even');
}
return Either.left('Not even');
};

const right = Either.right<number, string>('irrelevant').recover(recoverIfEven); // Right('irrelevant')

const leftEven = Either.left<number, number>(42).recover(recoverIfEven); // Right('Even')
const leftOdd = Either.left<number, number>(43).recover(recoverIfEven); // Left('Not even')
```

#### Running side effects

You can use the `onRight` method to run side effects on the value inside a `Right`.

```typescript
import { Either } from '@leanmind/monads';

const right = Either.right(42).onRight(x => console.log(x)); // 42
const left = Either.left('Error').onRight(x => console.log(x)); // No execution
```

Or you can use the `onLeft` method to run side effects on the value inside a `Left`.

```typescript
import { Either } from '@leanmind/monads';

const right = Either.right(42).onLeft(err => console.log(err)); // No execution
const left = Either.left('Error').onLeft(err => console.log(err)); // 'Error'
```

#### Folding an Either

You can use the `match` method to handle both `Right` and `Left` cases and unwrap the result.
You can use the `fold` method to handle both `Right` and `Left` cases and unwrap the result.

```typescript
import { Either } from '@leanmind/monads';

const sucess = Either.right(42).match(
err => `Error: ${err}`,
x => (x + 1).toString()
); // '43'
const success = Either.right<string, number>(42).fold({
ifRight: x => `${x + 1}`,
ifLeft: err => `Error: ${err}`,
}); // '43'

const error = Either.left('Error').match(
err => `Error: ${err}`,
x => (x + 1).toString(),
); // 'Error: Error'
const error = Either.left<string, number>('Error').fold({
ifRight: x => `${x + 1}`,
ifLeft: err => `Error: ${err}`,
}); // 'Error: Error'
```

#### checking if an Either is Right or Left
Expand Down Expand Up @@ -168,10 +215,10 @@ import { Either } from '@leanmind/monads';
const result = Either.right(42)
.map(x => x + 1)
.map(x => x * 2)
.match<string|number>(
err => `Error: ${err}`,
x => x
);
.fold({
ifRight: x => x.toString(),
ifLeft: err => `Error: ${err}`,
})

console.log(result); // 86
```
Expand All @@ -192,16 +239,16 @@ function divide(a: number, b: number): Either<string, number> {

const result = divide(10, 2)
.map(x => x * 2)
.match(
err => `Error: ${err}`,
value => `Result: ${value}`
);
.fold({
ifRight: x => `Result: ${x}`,
ifLeft: err => `Error: ${err}`,
});

console.log(result); // 'Result: 10'
```

In this example, the divide function returns an `Either` that represents the result of the division or an error if the
division is by zero. The result is then transformed and matched to produce a final `string`.
division is by zero. The result is then transformed and folded to produce a final `string`.

## Option Monad

Expand Down Expand Up @@ -244,7 +291,9 @@ none.getOrElse(0); // 0
You can use the `filter` method to keep the `Some` value if it satisfies a predicate.

```typescript
import { Option } from '@leanmind/monads';m
import { Option } from '@leanmind/monads';

m

const some = Option.of(42).filter(x => x > 40); // Some(42)
const none = Option.of(42).filter(x => x > 50); // None
Expand All @@ -257,7 +306,9 @@ You can use the `flatMap` or `map` method to transform the `Some` value.
##### Using `flatMap`

```typescript
import { Option } from '@leanmind/monads';m
import { Option } from '@leanmind/monads';

m

const some = Option.of(42).flatMap(x => Option.of(x + 1)); // Some(43)
const none = Option.of(null).flatMap(x => Option.of(x + 1)); // None
Expand All @@ -272,22 +323,42 @@ const some = Option.of(42).map(x => x + 1); // Some(43)
const none = Option.of(null).map(x => x + 1); // None
```

#### Matching an Option
#### Running side effects

You can use the `match` method to handle both `Some` and `None` cases and unwrap the result.
You can use the `onSome` method to run side effects on the value inside a `Some`.

```typescript
import { Option } from '@leanmind/monads';

const some = Option.of(42).match(
x => x + 1,
() => 'No value'
); // 43
const some = Option.some(42).onSome(x => console.log(x)); // 42
const none = Option.none().onSome(x => console.log(x)); // No execution
```

Or you can use the `onNone` method to run side effects on the value inside a `None`.

const none = Option.of(null).match(
x => x + 1,
() => 'No value'
); // 'No value'
```typescript
import { Option } from '@leanmind/monads';

const some = Option.some(42).onNone(_ => console.log('Empty value')); // No execution
const none = Option.none().onNone(_ => console.log('Empty value')); // 'Empty value'
```

#### Folding an Option

You can use the `fold` method to handle both `Some` and `None` cases and unwrap the result.

```typescript
import { Option } from '@leanmind/monads';

const some = Option.of(42).fold({
ifSome: x => `${x + 1}`,
ifNone: () => 'No value',
}); // '43'

const none = Option.of(null).fold({
ifSome: x => `${x + 1}`,
ifNone: () => 'No value',
}); // 'No value'
```

#### Checking if an Option is Some or None
Expand All @@ -312,7 +383,17 @@ The `Try` monad represents a computation that may fail.

### Usage

You can create a `Try` using the static method `Try.execute`.
you can create a `Try` using the static method `Try.success` or `Try.failure`.

```typescript
import { Try } from '@leanmind/monads';

const success = Try.success(42); // Success(42)

const failure = Try.failure(new Error('Error')); // Failure(Error('Error'))
```

Also, you can create a `Try` using the static method `Try.execute` from a function that may throw an exception.

```typescript
import { Try } from '@leanmind/monads';
Expand All @@ -324,47 +405,93 @@ const failure = Try.execute(() => {
}); // Failure(Error('Error'))
```

### Using `map`
#### Using `map`

You can use the `map` method to transform the value inside a `Success`.

```typescript
import { Try } from '@leanmind/monads';m
import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42).map(x => x + 1); // Success(43)
m

const success = Try.success(42).map(x => x + 1); // Success(43)
```

### Using `flatMap`
#### Using `flatMap`

You can use the `flatMap` method to transform the value inside a `Success` with a fallible closure.

```typescript
import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42).flatMap(x => Try.execute(() => x + 1)); // Success(43)
const success = Try.success(42).flatMap(x => Try.success(x + 1)); // Success(43)
```

### Matching a Try
#### Running side effects

You can use the `match` method to handle both `Success` and `Failure` cases and unwrap the result.
You can use the `onSuccess` method to run side effects on the value inside a `Success`.

```typescript
import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42).match(
err => `Error: ${err}`,
x => x + 1
); // 43
const succcess = Try.succcess(42).onSuccess(x => console.log(x)); // 42
const failure = Try.failure('Error').onSuccess(x => console.log(x)); // No execution
```

const failure = Try.execute(() => {
throw new Error('Error');
}).match(
err => `Error: ${err}`,
x => x + 1
); // 'Error: Error'
Or you can use the `onFailure` method to run side effects on the value inside a `Failure`.

```typescript
import { Try } from '@leanmind/monads';

const succcess = Try.succcess(42).onFailure(err => console.log(err)); // No execution
const failure = Try.failure(new Error('Error')).onFailure(err => console.log(err)); // Error('Error')
```

### Handling errors in Infrastructure code
#### Retrieving the value

You can use the `getOrElse` method to retrieve the value of a `Success` or provide a default value if it is `Failure`.

```typescript
import { Try } from '@leanmind/monads';

const success = Try.success(42);
const value = success.getOrElse(0); // 42

const failure = Try.failure(new Error('Error'));
const otherValue = failure.getOrElse(0); // 0
```

Also, you can use the `getOrThrow` method to retrieve the value of a `Success` or throw the error if it is `Failure`.

```typescript
import { Try } from '@leanmind/monads';

const success = Try.success(42);
const value = success.getOrThrow(); // 42

const failure = Try.failure(new Error('Error'));
const otherValue = failure.getOrThrow(); // throws Error('Error')
```

#### Folding a Try

You can use the `fold` method to handle both `Success` and `Failure` cases and unwrap the result.

```typescript
import { Try } from '@leanmind/monads';

const success = Try.success(42).fold({
ifSuccess: x => `${x + 1}`,
ifFailure: err => `Error: ${err}`,
}); // '43'

const failure = Try.failure(new Error('an error')).fold({
ifSuccess: x => `${x + 1}`,
ifFailure: err => `Error: ${err}`,
}); // 'Error: an error'
```

#### Handling errors in Infrastructure code

Normally, Try is used to handle `Exceptions` that are raise by third party libraries

Expand All @@ -374,15 +501,15 @@ import { Try } from '@leanmind/monads';
const result = Try.execute(() => {
// Some API of a library that may throw an exception
return 42;
}).match(
err => `Error: ${err}`,
x => x + 1
);
}).fold({
ifSuccess: x => `${x + 1}`,
ifFailure: err => `Error: ${err.message}`,
})

console.log(result); // 43
```

### Checking if a Try is Success or Failure
#### Checking if a Try is Success or Failure

If needed, you can check explicitly if a `Try` is `Success` or `Failure` using the `isSuccess` and `isFailure` methods.

Expand Down Expand Up @@ -479,6 +606,7 @@ So, you can operate as pure functions until you call the `runUnsafe` method.
### Usage

#### Creating an IO

You can create an `IO` using the static method `IO.of`.

```typescript
Expand Down
Loading
Loading