https://github.com/rametta/srd
:rocket: Simple Remote Data (SRD) is a fully static land compliant implementation of the Remote Data type in TypeScript
https://github.com/rametta/srd
functional hkt remotedata static-land typescript
Last synced: about 2 months ago
JSON representation
:rocket: Simple Remote Data (SRD) is a fully static land compliant implementation of the Remote Data type in TypeScript
- Host: GitHub
- URL: https://github.com/rametta/srd
- Owner: rametta
- License: bsd-3-clause
- Created: 2020-10-31T10:33:22.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2021-01-30T15:06:46.000Z (over 4 years ago)
- Last Synced: 2025-04-14T09:23:48.267Z (about 2 months ago)
- Topics: functional, hkt, remotedata, static-land, typescript
- Language: TypeScript
- Homepage:
- Size: 132 KB
- Stars: 28
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](http://npm.im/srd)
[](https://bundlephobia.com/result?p=srd)
[](https://opensource.org/licenses/BSD-3-Clause)
# 🚀 Simple Remote Data
Simple Remote Data (SRD) is a fully [static land](https://github.com/fantasyland/static-land/blob/master/docs/spec.md) compliant implementation of the Remote Data type in TypeScript - built with [Higer Kinded Types]() (HKT's) inspired by [fp-ts](https://github.com/gcanti/fp-ts) and [Elm Remote Data](https://package.elm-lang.org/packages/krisajenkins/remotedata/6.0.1/RemoteData).
The idea for using HKT's in TypeScript is based on [Lightweight higher-kinded polymorphism](https://www.cl.cam.ac.uk/~jdy22/papers/lightweight-higher-kinded-polymorphism.pdf).
##
Static Land Compliant
## Install
With yarn
```sh
yarn add srd
```or if you prefer npm
```sh
npm i srd
```or if you don't like bundlers, no need to install, just import directly from a CDN:
```html
import { SRD } from 'https://cdn.skypack.dev/srd';
```
SRD supports [CJS](https://requirejs.org/docs/commonjs.html), [UMD](https://github.com/umdjs/umd) and [ESM](https://webpack.js.org/guides/ecma-script-modules/) bundle outputs.
## Examples
### React Example
```jsx
import React, { useState, useEffect } from 'react'
import { SRD, notAsked, loading, failure, success } from 'srd'const App = () => {
const [rd, setRd] = useState(notAsked())useEffect(() => {
setRd(loading())
fetch('...')
.then((data) => setRd(success(data)))
.catch((err) => setRd(failure(err)))
}, [])return SRD.match({
notAsked: () =>Empty,
loading: () =>Loading...,
failure: (err) =>{err},
success: (data) =>{data},
}, rd)
}
```### Typescript React Example
SRD works even better with Typescript! Declare your RD type once and have typescript powerfully infer it everywhere! Like magic!
```tsx
import React, { useState, useEffect } from 'react'
import { SRD, RD, notAsked, loading, failure, success } from 'srd'
import { Person, getPerson } from './people'const App = () => {
const [rd, setRd] = useState>(notAsked())useEffect(() => {
setRd(loading())
getPerson(123)
.then((person) => setRd(success(person)))
.catch((err) => setRd(failure(err)))
}, [])return SRD.match({
notAsked: () =>Empty,
loading: () =>Loading...,
failure: (msg) =>{msg},
success: (person) =>{person},
}, rd)
}
```## Documentation
`SRD` comes with many of the Static Land functions that we all know and love. Here is a breakdown of all the supported algebras and utilities:
### Setoid
For comparing 2 SRD's to see if they are the same type.
> *Note: This only compares the data types and not the inner value. So `Success(5) != Failure(5)` but `Success(5) == Success(80)`.
```hs
equals :: (RD e a, RD e b) -> boolean
``````ts
import { SRD, success, notAsked } from 'SRD'SRD.equals(success(5), notAsked()) // false
```### Functor
Allowing the `SRD` to be `mapped` over by the function provided.
```hs
map :: (a -> b, RD e a) -> RD e b
``````ts
import { SRD, success, loading } from 'SRD'const double = x => x * 2
const rd1 = success(4)
const rd2 = loading()SRD.map(double, rd1) // success(8)
SRD.map(double, rd2) // loading()
```### Bifunctor
Allowing the type to be `bimapped` over by the functions provided. Common usecase is for when you need to `map` and `mapFailure` in one shot.
```hs
bimap :: (e -> b, a -> c, RD e a) -> RD b c
``````ts
import { SRD, success, failure } from 'SRD'const double = x => x * 2
const formatErr = err => `Something went wrong: ${err}`
const rd1 = success(4)
const rd2 = failure('404 not found')SRD.bimap(formatErr, double, rd1) // success(8)
SRD.bimap(formatErr, double, rd2) // failure('Something went wrong: 404 not found')
```### Apply
Apply a function wrapped in a SRD to a value wrapped in a SRD.
```hs
ap :: (RD e (a -> b), RD e a) -> RD e b
``````ts
import { SRD, success, failure } from 'SRD'const double = x => x * 2
const formatErr = err => `Something went wrong: ${err}`
const rd1 = success(4)
const rd2 = failure('404 not found')SRD.ap(success(double), rd1)) // success(8)
SRD.ap(success(double), rd2)) // failure('404 not found')
```### Applicative
Always returns a `success` with whatever value is passed within.
```hs
of :: a -> RD e a
``````ts
import { SRD } from 'SRD'SRD.of(4) // success(4)
```### Alt
Provide a default value to be returned when an `SRD` is not a `success` type.
```hs
alt :: (RD e a, RD e a) -> RD e a
``````ts
import { SRD, success, loading, notAsked } from 'SRD'SRD.alt(success(4), notAsked()) // success(4)
SRD.alt(success(50), success(4)) // success(4)
SRD.alt(loading(), notAsked()) // loading()
SRD.alt(loading(), success(4)) // success(4)
```### Chain
Similar to `map` but the callback must return another `SRD`.
```hs
chain :: (a -> RD e b, RD e a) -> RD e b
``````ts
import { SRD, success, failure, notAsked } from 'SRD'SRD.chain(x => success(x * 2), success(4)) // success(8)
SRD.chain(x => success(x * 2), notAsked()) // notAsked()
SRD.chain(x => failure('failed'), success(4)) // failure('failed')
```### Match
Provide a mapper object for each SRD type and whichever type the SRD is - that function will run.
```hs
data Matcher e a ::
{ notAsked :: () -> c
, loading :: () -> c
, failure :: e -> c
, success :: a -> c
}match :: (Matcher e a -> c, RD e a) -> c
``````ts
import { SRD, success } from 'SRD'SRD.match({
notAsked: () => 'Empty',
loading: () => 'Loading...',
failure: e => `Err: ${e}`,
success: data => `My data is ${data}`
}, success(4)) // My data is 4
```### Map Failure
Similar to `map` but instead of running the callback on a `success`, it calls it on a `failure`.
```hs
mapFailure :: (e -> b, RD e a) -> RD b a
``````ts
import { SRD, success, failure } from 'SRD'SRD.mapFailure(x => `hello ${x}`, success(4)) // success(4)
SRD.mapFailure(x => `hello ${x}`, failure('bob')) // failure('hello bob')
```### Map2
Similar to `map` but takes 2 `SRD's` instead of one, and if both are a `success`, the provided callback will be called.
```hs
map2 :: (a b -> c, RD e a, RD e b) -> RD e c
``````ts
import { SRD, success, failure } from 'SRD'SRD.map2((x, y) => x + y, success(4), success(8)) // success(12)
SRD.map2((x, y) => x + y, failure('bob'), success(8)) // failure('bob')
SRD.map2((x, y) => x + y, success(8), failure('bob')) // failure('bob')
```### Map3
Similar to `map2` but takes 3 `SRD's` instead of two, and if all three are a `success`, the provided callback will be called.
```hs
map3 :: (a b c -> d, RD e a, RD e b, RD e c) -> RD e d
``````ts
import { SRD, success, failure, notAsked, loading } from 'SRD'const add3 = (x, y, z) = x + y + z
SRD.map3(add3, success(4), success(8), success(10)) // success(22)
SRD.map3(add3, failure('bob'), success(8), notAsked()) // failure('bob')
SRD.map3(add3, success(8), loading(), failure('bob')) // loading()
```### Unwrap
Similar to `alt`, but unwraps the SRD from it's type and runs the callback on it. If the SRD is a `success` the inner value is passed to the callback and returned, any other value the default is returned.
```hs
unwrap :: (b, a -> b, RD e a) -> b
``````ts
import { SRD, success, notAsked, loading } from 'SRD'const double = x => x * 2
SRD.unwrap(6, double, success(8)) // 16
SRD.unwrap(6, double, notAsked()) // 6
SRD.unwrap(6, double, loading()) // 6
```### Unpack
Similar to `unwrap`, but takes a default thunk instead of a default value.
```hs
unpack :: (() -> b, a -> b, RD e a) -> b
``````ts
import { SRD, success, notAsked, loading } from 'SRD'const double = x => x * 2
SRD.unpack(() => 6, double, success(8)) // 16
SRD.unpack(() => 6, double, notAsked()) // 6
SRD.unpack(() => 6, double, loading()) // 6
```### WithDefault
Takes a default value and an SRD. If the SRD is a `success` then the inner value is returned, otherwise the default value is returned.
```hs
withDefault :: (a, RD e a) -> a
``````ts
import { SRD, success, notAsked, loading } from 'SRD'SRD.withDefault(4, success(8)) // 8
SRD.withDefault(4, notAsked()) // 4
SRD.withDefault(4, loading()) // 4
```### IsSuccess
Takes an SRD and returns a boolean if it is a `success` type.
```hs
isSuccess :: (RD e a) -> bool
``````ts
import { SRD, success, notAsked } from 'SRD'SRD.isSuccess(success(8)) // true
SRD.isSuccess(notAsked()) // false
```### IsFailure
Takes an SRD and returns a boolean if it is a `failure` type.
```hs
isFailure :: (RD e a) -> bool
``````ts
import { SRD, success, failure } from 'SRD'SRD.isFailure(success(8)) // false
SRD.isFailure(failure()) // true
```### IsNotAsked
Takes an SRD and returns a boolean if it is a `notAsked` type.
```hs
isNotAsked :: (RD e a) -> bool
``````ts
import { SRD, success, notAsked } from 'SRD'SRD.isNotAsked(success(8)) // false
SRD.isNotAsked(notAsked()) // true
```### IsLoading
Takes an SRD and returns a boolean if it is a `loading` type.
```hs
isLoading :: (RD e a) -> bool
``````ts
import { SRD, success, loading } from 'SRD'SRD.isLoading(success(8)) // false
SRD.isLoading(loading()) // true
```