npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

exalted.future

v0.0.36

Published

Exalted monadic library & functional fun, fantasy-land compliant, mostly.

Downloads

12

Readme

Experimental The api can change between versions.

exalted.future

A javascript monadic library & functional fun. fantasy-land compliant, mostly.

The style of monad object is inspired by DrBoolean course on egghead.io and his book Mostly adequate guide to FP.

The choice for cata, encase, head, tail, last is inspired by rametta's take on monads in pratica.

This is, in many ways, an evolution of oncha which I wrote with other people many years ago and is no longer maintained.

Natural Transformation

One of the main goals of the exalted.future is to make it possible to rely on natural transformation when composing Monads. So that you can write in one continuous flow your program, agnostic of the Monad you are working with. That is why flatMap, mapReduce, reduce, fold, and fork use the same language, cata. You can always call cata on an object, and it will compute your results. The following example attempts to illustrate that. Regardless that the fetch succeeds or fails the outcome will be the same, indifferent to calling cata on Maybe, Either (Left|Right), or Future.

Future.promise(fetch('https://jsonplaceholder.typicode.com/todos/1'))
  .chain(response => Either.encase(() => response.json()))
  .chain(Future.promise)
  .chain(Maybe)
  .map(todo => todo.title)
  .map(t => t.toUpperCase())
  .cata({
    Left: e => console.log('oops', e),
    Right: json => console.log(json)
  })

Install

yarn add exalted.future

Types

| Name | Apply | Applicative | Setoid | Foldable| Functor | Monad | Chain | | ----------------- | :----------: | :--------------: | :----------: | :----------: | :----------: | :--------: | :-----------: | | Either | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | | Future | ✔︎ | — | ✔︎ | ✔︎ | ✔︎ | — | ✔︎ | | Identity | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | | Maybe | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ | ✔︎ |

  • There is a divergence from fantasy-land where reduce is named cata and loosely based on daggy's union types.
  • fold always folds on identity a => a, except when it does not like with the Future.
  • Maybe.map will return Nothing if the callback function returns null. In other words Just(null) is impossible, unless you call the 'static' constructor like this Just.of(null). See this pr for some explanation.
  • Left is not 100% applicative.
  • Not all functions are documented, so you are encouraged to read the source code, you'll find bimap, swap, fold, foldr, foldf.

Future

A Future monad for async computation. Left is reject and Right is resolve. Because Right is always right and Left is not.

// Basic usage
Future((err, ok) => ok('Yay'))
  .map(res => res.toUpperString())
  .cata({
    Left: err => log(`Err: ${err}`),
    Right: res => log(`Res: ${res}`)
  })
//=> 'YAY'

// Handle promises
Future.promise(fetch('https://api.awesome.com/catOfTheDay'))
  .cata({
    Left: err => log('There was an error fetching the cat of the day :('),
    Right: cat => log('Cat of the day: ' + cat)
  })
//=> 'Cat of the day: Garfield'

// Chain http calls
Future.promise(fetch('https://api.awesome.com/catOfTheDay'))
  .chain(cat => Future.promise(fetch(`https://api.catfacts.com/${cat}`)))
  .cata({
    Left: err => log('There was an error fetching the cat of the day :('),
    Right: facts => log('Facts for cat of the day: ' + facts)
  })
//=> 'Facts for cat of the day: Garfield is awesome.'

Future.all

Concats all the results form the list of futures.

all :: ([Futures]) -> b
Future.all(
  Future.of('apple'),
  Future((left, right) => setTimeout(() => right('orange'), 1000)),
  Future.of('lemon')
).cata({
    Left: () => (),
    Right: ([ apple, orange, lemon ]) => console.log(apple, orange, lemon)
  }) //=> apple, orange, lemon

Id

Identity monad.

Id(5)
  .map(num => num * 7)
  .map(num => num - 1)
  .cata({
    Right: a => a
  })
//=> 34

Maybe

Maybe monad.

// Maybe of a string
Maybe('Hello exalted one')
  .map(sentence => sentence.toUpperString())
  .map(sentence => `${sentence}!`)
  .cata({
    Right: console.log
  })
//=> 'HELLO EXALTED ONE!'

// Maybe of nothing
Maybe(null)
  .map(sentence => sentence.toUpperString())
  .alt(() => 'Maybe received a null')
  .cata({
    Right: console.log
  })
//=> 'Maybe received a null'

Either

An Either monad and nullable, Left, Right.

nullable('Hello') // this will return a Right('Hello')
  .cata({
    Left: () => 'Oops',
    Right: val => `${val} world!`
  })
//=> 'Hello world!'

nullable(null) // this will return a Left()
  .cata({
    Left: () => 'Oops',
    Right: val => `${val} world!`
  })
//=> 'Oops'

const extractEmail = obj => obj.email ? Right(obj.email) : Left()
extractEmail({ email: '[email protected]' }
  .map(extractDomain)
  .cata({
    Left: () => 'No email found!',
    Right:x => x
  })
//=> 'example.com'

extractEmail({ name: 'user' }
  .map(extractDomain) // this will not get executed
  .cata({
    Left: () => 'No email found!',
    Right: x => x
  })
//=> 'No email found!'

Functions

The following functions are common to all monads types.

alt

Sets the value to cata on.

alt :: Any -> Nothing of Any
Maybe(1).alt(5).cata({
    Right: a => a
  })
//=> 1

Maybe(null).alt(5).cata({
    Right: a => a
  })
//=> 5

ap

Apply

chain :: (a -> b) -> b
Id(5).chain(a => Id(a))
//=> Id(5)

// You can use chain to join the monads.
Id(Id(5)).chain(a => a)
//=> Id(5)

cata

Foldable

cata :: ({ Left: () -> b, Right -> a -> a }) -> a | b
Id(5).cata({
    Right: a => a
  })
//=> 5

Id(5).cata({
    Right: a => a + 1
  })
//=> 6

Right(5).cata({
    Left: a => 8 // ignored 
    Right: a => a + 1
  })
//=> 6

Left(5).cata({
    Left: a => a + 1 
    Right: a => 8 // ignored
  })
//=> 6

Maybe(5).cata({
    Right: a => a
  })
//=> 5

Maybe(5).cata({
    Left: () => { } // not called
    Right: a => a + 1
  })
//=> 6

Maybe(null).cata({
    Left: () => 'there was a null'
    Right: a => a + 1 // not called
  })
//=> there was a null

Right(5).cata({
    Left: () => 1,
    Right: a => a + 2
  })
//=> 7

Left(5).cata(a => a + 1)
//=> 6

chain

Chain

chain :: (a -> b) -> b
Id(5).chain(a => Id(a))
//=> Id(5)

// You can use chain to join the monads.
Id(Id(5)).chain(a => a)
//=> Id(5)

compose

Compose takes n functions as arguments and return a function.

const transform = compose(sentence => sentence.toUpperString(), sentence => `${sentence}!`)
const logTransform = compose(log, transform)

logTransform('Hello exalted one')
//=> 'HELLO EXALTED ONE!'

// supports miltiple arguments
compose(path.normalize, path.join)('./exalted', '/one')
//=> './exalted/one'

equals

Setoid

equals :: Id -> Boolean
Id(1).equals(Id(1))
//=> true

Id(2).equals(Id(1))
//=> false

Id(2).equals(Id(1)) === Id(1).equals(Id(1))
//=> false

inspect

inspect :: () -> String
Id(5).inspect()
//=> Id(5)

map

Functor

map :: (a -> b) -> Id of b
Id(7).map(a => a * 2)
//=> Id(14)

map (first class)

Map as partial application and first class with arity support.

map(a => a + 1, a => a * 3)([1, 2, 3])
//=> [4, 7, 10]

of

Applicative

of :: a -> Id of a
Id(5).of(6)
//=> Id(6)

Id(5).of(Id(6))
//=> Id(Id(6))

head, tail, last

Returns a Maybe.

head([1,2])
//=> Just(1)

head([])
//=> Nothing()

tail([1,2,3])
//=> Just([2,3])

tail([])
//=> Nothing()

last([1,2,3])
//=> Just(3)

last([])
//=> Nothing()

Maybe.encase, Either.encase

Returns Left | Right.

Maybe.encase(() => JSON.parse('["foo","bar","baz"]'))
//=> Just(['foo','bar','baz'])

Maybe.encase(() => JSON.parse('['))
//=> Nothing()

Either.encase(() => JSON.parse('["foo","bar","baz"]'))
//=> Right(['foo','bar','baz'])

Either.encase(() => JSON.parse('['))
//=> Left(new SyntaxError ('Unexpected end of JSON input'))