https://github.com/nythrox/purifree
Pointfree type-safe functional programming library for TypeScript - with do notation, HKTs, generic lifts and more
https://github.com/nythrox/purifree
fp-ts functional-programming javascript typescript
Last synced: 29 days ago
JSON representation
Pointfree type-safe functional programming library for TypeScript - with do notation, HKTs, generic lifts and more
- Host: GitHub
- URL: https://github.com/nythrox/purifree
- Owner: nythrox
- License: isc
- Created: 2020-09-15T13:07:43.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-06-27T18:00:20.000Z (over 3 years ago)
- Last Synced: 2025-12-27T01:43:16.750Z (3 months ago)
- Topics: fp-ts, functional-programming, javascript, typescript
- Language: TypeScript
- Homepage:
- Size: 2.27 MB
- Stars: 93
- Watchers: 1
- Forks: 3
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Purifree
Purifree is a fork from Purify that allows you to program in a point-free style, and adds a few new capabilities.
# What is Purify?
Purify is a library for functional programming in TypeScript.
Its purpose is to allow developers to use popular patterns and abstractions that are available in most functional languages.
Learn more about Purify here
# How to start?
Purifree is available as a package on npm. You can install it with a package manager of your choice:
```
$ npm install purifree-ts
```
# Purifree compatability
Purifree is 100% compatible with purify, and can be used interchangeably.
## Point-free style
Point-free functions can be used with any ADTs (without needing module-specific imports), and can also be used together with the chainable (purify) API.
```typescript
// pointfree: Maybe
const pointfree = pipe(
Just('name'),
map((name) => name.toUpperCase()),
filter((name) => name.length > 5),
chain((name) => (Math.random() > 0.5 ? Just(name + ' lucky :)') : Nothing))
)
// matchTest: string
const matchTest = pipe(
Right(100),
chain((num) => (num > 50 ? Right(num) : Left(`bad number: ${num}`))),
match({
Right: (e) => 'Great number!' + e,
Left: (e) => `OK number. | msg: (${e})`
})
)
```
## Do* notation
This fork features the generator do* notation for all data structures except for arrays.
The do notation lets you easily chain operations without having to nest your code.
```typescript
// result: Either
const result = Do(function* () {
// name: string
const name = yield* Right("name")
// surname: string
const surname = yield* Right("surname")
// favoriteColor: string
const favoriteColor = yield* Left(Error("DB error!"))
return {
name,
surname,
favoriteColor
}
})
```
Chain version equivalent:
```typescript
// result: Either
const result = Right('name').chain((name) =>
Right('surname').chain((surname) =>
Left(Error('DB error!')).map((favoriteColor) => ({
name,
surname,
favoriteColor
}))
)
)
```
### Traverse, Sequence, SequenceS, SequenceT
```typescript
// Gets an Either, maps it to an Either>, and inverts it into a NonEmptyList>
// traverseTest: NonEmptyList>
const traverseTest = pipe(
Right(1),
traverse(NonEmptyList, (num) => NonEmptyList(num))
)
// Gets an Either> and inverts it into a NonEmptyList>
// sequenceTest: NonEmptyList>
const sequenceTest = pipe(
Right(NonEmptyList(1)),
sequence(NonEmptyList)
)
// sequenceTTest: Either
const sequenceTTest = sequenceT(Either.of)(Right(2), Right('name'), Right(true))
// sequenceStrutureTest: Either
const sequenceStrutureTest = sequenceS(Either.of)({
name: Right('name'),
age: Right(100)
})
```
### Kleisli
The function pipeK can be used as an easy way to combine functions that return monads without using chain.
If you need to use a long list of chains, you can use the pipeK function to compose the functions instead of passing each one into chain.
Instead of:
```typescript
const getNameTest = pipe(
chain((name?: string) => name ? Just(name) : Nothing),
chain((name) => Just(name.toUpperCase())),
chain((uppercasedName) => uppercasedName.length > 3 ? Just(uppercasedName) : Nothing)
)
```
Use:
```typescript
// getNameTest: ( name?: string ) => Maybe
const getNameTest = kleisli(
(name?: string) => name ? Just(name) : Nothing,
(name) => Just(name.toUpperCase()),
(uppercasedName) => uppercasedName.length > 3 ? Just(uppercasedName) : Nothing
)
// result: Maybe
const result = getNameTest('name')
```
### Lifting
You can use the liftN family of functions to lift a function that takes normal values into a function that takes and returns elevated values.
*WARNING*: If you try lifting a function that uses generics, it will probably loose its type due to typescript's limitations.
```typescript
// add takes normal values
const add = (num1: number, num2: number) => num1 + num2
// addL takes elevated values, and returns an elevated value
// addL: Lifted<(a: Ap, b: Ap) => Ap>
const addL = lift2(add)
// add5Option (b: Either) => Either
const add5Option = addL(Right(5))
// result: Either = Right(15)
const result = add5Option(Right(10))
```
### Node for Codec
The Codec module's function `map` is re-exported as `Codec.map`
### Codesandbox
You can try it out in the browser.