Giter VIP home page Giter VIP logo

fp-ts's Introduction

Functional programming in TypeScript

build status npm downloads

Introduction

fp-ts is a library for typed functional programming in TypeScript.

fp-ts aims to allow developers to use popular patterns and abstractions that are available in most functional languages. For this, it includes the most popular data types, type classes and abstractions such as Option, Either, IO, Task, Functor, Applicative, Monad to empower users to write pure FP apps and libraries built atop higher order abstractions.

A distinctive feature of fp-ts with respect to other functional libraries is its implementation of Higher Kinded Types, which TypeScript doesn't support natively.

Inspired by

Sponsors

Unsplash
Unsplash
https://unsplash.com/

The internet’s source for visuals.
Powered by creators everywhere.

Installation

To install the stable version:

npm install fp-ts

Make sure to always have a single version of fp-ts installed in your project. Multiple versions are known to cause tsc to hang during compilation. You can check the versions currently installed using npm ls fp-ts (make sure there's a single version and all the others are marked as deduped).

TypeScript compatibility

Strictness – This library is conceived, tested and is supposed to be consumed by TypeScript with the strict flag turned on.

fp-ts version required typescript version
2.0.x+ 3.5+
1.15.x+ 3.1+
<= 1.14.4 2.8+ (*)

(*) If you are running < [email protected] you have to polyfill the unknown type. You can use unknown-ts as a polyfill.

Documentation

Disclaimer. Teaching functional programming is out of scope of this project, so the documentation assumes you already know what FP is.

Help

If you need help with fp-ts check out:

Development

License

The MIT License (MIT)

fp-ts's People

Contributors

bravely avatar bwlt avatar cdimitroulas avatar denisfrezzato avatar fmpanelli avatar gcanti avatar giogonzo avatar grossbart avatar joshburgess avatar jussisaurio avatar leemhenson avatar maximervy avatar newswim avatar oliverjash avatar piq9117 avatar punie avatar raveclassic avatar simonmeskens avatar sledorze avatar srachamim avatar sukovanej avatar thewilkybarkid avatar thomasvargiu avatar vicrac avatar vinassefranche avatar waynevanson avatar willheslam avatar wmaurer avatar xkollar avatar ybogomolov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fp-ts's Issues

High CPU load in VSCode

Often when editing code using fp-ts, vscode go crazy on CPU during several seconds.
This is a reduced case, it can last way longer with more involved code.

Minimal repro repo:
https://github.com/sledorze/vscode-hanging

Main code showing the issue :

import {
Validation
} from 'fp-ts/lib/Validation';

import {
array,
validation,
} from 'fp-ts';

export function pouet(
xs: number[],
f: (x: number) => Validation<string[], number>
): void {
array.traverse(validation. .getApplicativeS(array))(f, xs); // Long hanging due to this space!
}

How to flatten an array of options?

Hi there, thanks for the fantastic library.

I have an array of options and I want to flatten it to an array of the Some values, filtering out the Nones.

How would you recommend doing this?

Is this something you think could be added to the library?

Thanks,
Oliver

Either.chainLeft

I have an Either where, if its a Right instance, I want to map the inner value and transform the Either instance to a Left. I am currently using fold to do this, but wondered if there was a better pattern. Perhaps chainLeft would make sense here, like mapLeft?

const e = either.left<string, string>('foo')
e
  .fold(
    left => either.left<string, string>(left),
    right => either.left<string, string>(mapFn(right))
  )

Using tuples with sequences

I have a tuple of two options, one containing string and one containing number. I want to sequence them, and then access the sequenced tuple type from functions such as map.

Example:

{
    const typecheck = <T>(t: T) => t;

    type OptionTuple2 = tuple.Tuple<option.Option<string>, option.Option<number>>;
    const optionTuple2 = typecheck<OptionTuple2>([
        option.some('foo'),
        option.some(1),
    ]);

    const sequenceOptionArray = traversable.sequence(option, array);
    sequenceOptionArray(optionTuple2); // errors
    sequenceOptionArray<[string, number]>(optionTuple2); // errors
    sequenceOptionArray<string | number>(optionTuple2); // works
}

Ideally, when mapping over the result of sequenceOptionArray, the type of the value in the sequenced Option would be the sequenced tuple:

const sequenced = sequenceOptionArray(optionTuple2);
sequenced.map(([ str, num ]) => str)

Do you have any idea if this is possible with TypeScript?

Monoid: add getFunctionStaticMonoid

Reference http://www.fewbutripe.com/swift/math/algebra/monoid/2017/04/18/algbera-of-predicates-and-sorting-functions.html

The set of all functions from a fixed type into a monoid can be naturally made into a monoid

// Monoid module

export function getFunctionStaticMonoid<M>(monoid: StaticMonoid<M>): <A>() => StaticMonoid<(a: A) => M> {
  const empty = constant(constant(monoid.empty()))
  return <A>(): StaticMonoid<(a: A) => M> => ({
    empty,
    concat: (f, g) => a => monoid.concat(f(a), g(a))
  })
}

Usage

const getPredicateStaticMonoidAll = getFunctionStaticMonoid(monoidAll)
const getPredicateStaticMonoidAny = getFunctionStaticMonoid(monoidAny)

const isLessThan10 = (n: number) => n <= 10
const isEven = (n: number) => n % 2 === 0

const monoidPredicateAllNumber = getPredicateStaticMonoidAll<number>()
const isLessThan10AndEven = monoidPredicateAllNumber.concat(isLessThan10, isEven)

console.log(array.filter(isLessThan10AndEven, [1, 2, 3, 40])) // [ 2 ]

console.log(array.filter(getPredicateStaticMonoidAny<number>().concat(isLessThan10, isEven), [1, 2, 3, 40])) // [ 1, 2, 3, 40 ]

Change contramap signature

To

contramap<A>(fa: HKT<A>[F]): <B>(f: (b: B) => A) => HKT<B>[F]

The current signature leads to unsafe code

type Predicate<A> = (a: A) => boolean

declare function contramap<A, B>(fa: Predicate<A>, f: (a: B) => A): Predicate<B>
declare function contramap2<A>(fa: Predicate<A>): <B>(f: (a: B) => A) => Predicate<B>

declare var o: Predicate<{ foo: string, bar: number }>

declare function f(): { foo: string }

contramap(o, f) // NO ERROR!
contramap2(o)(f) // ok, error: Property 'bar' is missing in type '{ foo: string; }'

task with other promise implementations

Hi.

We're using Bluebird as our promise implementation in a project, and I'd like to use Task in there too. Alas, Task enforces standard ES6 promises. At the moment, I've basically copied the entire Task.ts file into my own project and s/Promise/Bluebird/ and s/Task/BTask/. This is working fine for me, but obviously I'd be better off if fp-ts supported either promise library natively.

I think supporting both seems tricky though, unless you know of some magic generics incantation or something.....

Any ideas?

Cheers

Add Store comonad

class Store<S, A> {
  constructor(
    public readonly get: (s: S) => A,
    public readonly s: S
  ) {}
}

add Tree module

Where

data BinaryTree a = Empty | Node a (Tree a) (Tree a)
  • Monad
  • Foldable
  • Traversable

add indexed monad

This is the first step in order to port hyper to fp-ts

Reference implementation: https://github.com/garyb/purescript-indexed-monad

What's an indexed monad: http://stackoverflow.com/questions/28690448/what-is-indexed-monad

Usage

Based on State Machines All The Way Down
An Architecture for Dependently Typed Applications
https://eb.host.cs.st-andrews.ac.uk/drafts/states-all-the-way.pdf
by Edwin Brady

import * as ixio from 'fp-ts/lib/IxIO'
import * as io from 'fp-ts/lib/IO'

//
// finite state machine
//

// By defining this state machine in a type, we can ensure that any sequence
// of operations which type checks is a valid sequence of operations on a door.
// The door can be open or closed
type DoorState =
  | 'Open'
  | 'Closed'

//
// operations
//

// A  represents the return type of the operation
// I represents the input state (the precondition)
// O represents output state (the postcondition)
class Operation<I extends DoorState, O extends DoorState, A> extends ixio.IxIO<I, O, A> {
  constructor(a: A, o: O) {
    super(i => new io.IO(() => {
      console.log(`The door was ${i} and now is ${o}`)
      return [a, o] as [A, O]
    }))
  }
}

class Open extends Operation<'Closed', 'Open', void> {
  constructor() {
    super(undefined, 'Open')
  }
}
class Close extends Operation<'Open', 'Closed', void> {
  constructor() {
    super(undefined, 'Closed')
  }
}
class RingBell extends Operation<'Closed', 'Closed', void> {
  constructor() {
    super(undefined, 'Closed')
  }
}

// error: Type '"Open"' is not assignable to type '"Closed"'
// if you open the door, you must close it
const x1: Operation<'Closed', 'Closed', void> = new Open()

// ok
const x2: Operation<'Closed', 'Closed', void> = new Open().ichain(() => new Close())

// error: Type '"Closed"' is not assignable to type '"Open"'
// you can't ring the bell when the door is open
const x3 = new Open().ichain(() => new RingBell())

// ok
const x4 = new Open().ichain(() => new Close()).ichain(() => new RingBell())

x4.run('Closed').run()
/*
The door was Closed and now is Open
The door was Open and now is Closed
The door was Closed and now is Closed
*/

Serialization/deserialization of data types

This is what I get from Option now

import { fromNullable } from 'fp-ts/lib/Option'

const x = fromNullable(1)
const y = fromNullable(null)

console.log(JSON.stringify(x)) // => {"value":1}
console.log(JSON.stringify(y)) // => {}

In order to behave more like flow-static-land which doesn't change the underline representation when you call inj / prj, would be useful a toNullable function

declare function toNullable<A>(fa: HKTOption<A>): A | null

such that the pair inj / prj is equivalent to the pair fromNullable / toNullable. Also both None and Some could implement the toJSON method.

Proof of concept

export class None<A> {
  ...
  toJSON(): null {
    return null
  }
}

export class Some<A> {
  ...
  toJSON(): A {
    return this.value
  }
}

export function fromNullable<A>(a: A | null | undefined): Option<A> {
  return a == null ? none : new Some(a)
}

export function toNullable<A>(fa: HKTOption<A>): A | null {
  return (fa as Option<A>).toJSON()
}

Result

const x = fromNullable(1)
const y = fromNullable(null)

console.log(toNullable(x)) // => 1
console.log(toNullable(y)) // => null

console.log(JSON.stringify(x)) // => 1
console.log(JSON.stringify(y)) // => null

danielepolencic's comment here gcanti/io-ts#31

Initially this is what I had.

The problem I faced is that - while the .toJSON solves the serialisation - there's no easy way to
deserialise the data structure.

As an example, I'm using a redux-like framework and messages and state are plain objects by definition. When I wrapped my Maybes into classes, I had to > have ser/deser functions all over codebase. This made the code more complicated and bloated.

For data types such as Maybe and Either, it makes sense to have the data type as a plain object. In that way there's no need to transform the data into the proper data type.

add Trans typeclass (liftT)

PureScript

class MonadTrans t where
  lift :: (Monad m) => m a -> t m a

TypeScript

import { HKT, HKTS, HKT2, HKT2S } from 'fp-ts/lib/HKT'
import { StaticMonad } from 'fp-ts/lib/Monad'

export interface StaticTrans<T extends HKT2S> extends StaticMonad<T> {
  liftT<M extends HKTS>(monad: StaticMonad<M>): <A>(fa: HKT<A>[M]) => HKT2<M, A>[T]
}

Add Tuple module

Where

type Tuple<A, B> = [A, B]
  • Semigroupoid
  • Functor
  • Bifunctor
  • Extend
  • Comonad

A type inference pattern

The problem

The current implementation of foldMap has some type inference issues

import * as option from 'fp-ts/lib/Option'
import * as array from 'fp-ts/lib/Array'
import { monoidSum } from 'fp-ts/lib/Monoid'
import { foldMap } from 'fp-ts/lib/Foldable'

const monoid = option.getStaticMonoid(monoidSum)
const as = [1]
// x1 :: Option<{}> // <= {} instead of number
const x1 = foldMap(array, monoid, a => option.some(a), as)

Here TypeScript infers {} instead of number!

The solution

If I curry foldMap, the result is way better

export function foldMap$<F extends HKTS, M>(foldable: StaticFoldable<F>, monoid: StaticMonoid<M>): <A>(f: (a: A) => M, fa: HKT<A>[F]) => M {
  return <A>(f: (a: A) => M, fa: HKT<A>[F]) => foldable.reduce((acc, x: A) => monoid.concat(f(x), acc), monoid.empty(), fa)
}

// x2 :: Option<number>
const x2 = foldMap$(array, monoid)(a => option.some(a), as)

The pattern

After some trials and errors, a clean pattern seems to emerge: whenever in PureScript there is a type constraint, in fp-ts we need a curried function accepting the static dictionaries as first arguments

PureScript

foldMap :: forall f a m. Foldable f, Monoid m => (a -> m) -> f a -> m

TypeScript

function foldMap<F extends HKTS, M>(foldable: StaticFoldable<F>, monoid: StaticMonoid<M>): <A>(f: (a: A) => M, fa: HKT<A>[F]) => M

This is good news: in general porting PureScript libraries to fp-ts is pretty straightforward (with the possible exception of highly polymorphic signatures)

Dependency on rxjs

Hey, this looks like a really nice FP library for TypeScript and would love to use it but I'm a bit reluctant to include a dependency on rxjs to my project.

It seems like only a small part of the library and could easily be separated into a separate module, is this something you would consider?

add Dictionary module

Where

export type Dictionary<A> = { [key: string]: A }
  • monoid
  • functor
  • foldable
  • traversable

Question: Using Task with Either or Validation

I have an example where I need to fetch some data asynchronously and then validate it, which I think lends itself to a type of Task<Either> and/or Task<Validation>. For example:

    const timeout = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
    const task = new fp.task.Task(() => timeout(3000).then(() => 6));

    const taskValidation = task.map(validateAbove5);
    const mappedTaskValidation = taskValidation.map(validation => {
        return validation.map(x => x + 5);
    });

    mappedTaskValidation.run().then(console.log);

What do you think to the idea of a new type that consolidates Task with Either or Validation?

For example, the Left/Failure of my Task could be a network error, or once I've flat mapped over it to do some validation, it could be a validation failure.

Without a single type to represent this pattern, I have to map over the task and then map over the validation, which is quite verbose and I don't really need that level of control.

Do you know if this idea exists in any other languages? I think Fluture has some elements of this idea.

Lifted EitherT value returns any type

See the comment in the code below:

import {
    apply,
    either,
    eitherT,
    task,
} from 'fp-ts';

const eitherTInstance1 = new eitherT.EitherT(task, task.of(either.of<number, string>('foo')))
const eitherTInstance2 = new eitherT.EitherT(task, task.of(either.of<number, string>('foo')))
const eitherTInstance3 = apply.liftA2(eitherT, (a: string) => (b: string) => a)(
    eitherTInstance1,
    eitherTInstance2,
);
eitherTInstance3.value // <-- this returns any (!)

Any idea why this is? I tried to follow the generics but got a bit lost.

Sequence EitherT

I am trying to sequence an array of EitherT:

const allsuccess = [
    fp.either.right<string, number>(1),
    fp.either.right<string, number>(2),
    fp.either.right<string, number>(3),
]
    .map(fp.task.of)
    .map(task => new fp.eitherT.EitherT(fp.task, task));

sequence(fp.eitherT, array)(allsuccess).run();

However, I get this error:

src/main.ts(174,14): error TS2345: Argument of type 'typeof "/Users/OliverJAsh/Development/temp/fp-ts-test/node_modules/fp-ts/lib/EitherT"' is not assignable to parameter of type 'StaticApplicative<"EitherT">'.
  Types of property 'of' are incompatible.
    Type '<M extends "Array" | "Option" | "Either" | "Const" | "Dictionary" | "EitherT" | "Free" | "Identit...' is not assignable to type '<A>(a: A) => EitherT<any, any, A>'.
      Type '<L, A>(a: A) => EitherT<any, L, A>' is not assignable to type 'EitherT<any, any, any>'.
        Property 'monad' is missing in type '<L, A>(a: A) => EitherT<any, L, A>'.

Should this be possible?

For context, I have a bunch of tasks that I want to fire of in parallel, but as soon as one of them fails, I want to respond with that failure immediately—instead of waiting for the other tasks to finish and then sequencing the results.

This would be similar to Promise.all, which will return a rejected promise as soon as one of the promises is rejected.

Convert Either to Validation

Is it possible to write a function that converts an Either to a Validation?

I presume this would only work if the Left type was a semigroup, as that is required by Validation?

Here was my attempt, however it fails to compile:

import { StaticSemigroup } from "fp-ts/lib/Semigroup";

const eitherToValidation = <L, A>(
    either: either.Either<StaticSemigroup<L>, A>,
): validation.Validation<L, A> => {
    return either
        .fold(
            leftSemigroup => (
                validation.failure<L, A>([], leftSemigroup)
            ),
            right => validation.success<L, A>(right),
        );
};

Library enhancement with typescript Predicates?

I am wondering how well it would fit into fp-ts to take advantage of narrowing with native predicate types.
For instance, I'm often using that kind of type-safe function:

const filterPredicate = <T, U extends T>(pred: (x: T) => x is U, arr: Array<T>): Array<U> =>
  arr.filter(pred) as Array<U>

If we adapt for for higher kinds, I don't really know how we would name it.

What do you think @gcanti ?

How to aggregate validations?

I have Array<Validation<A, B>> and want to aggregate all the successes and failures, e.g. Array<Validation<A, B>> => Validation<Array<A>, Array<B>>.

Do you have any ideas how I would do this?

Context: I have a bunch of async tasks that happen in parallel, and if one or more fail, I want all the failures to display to the user—otherwise I want all the successes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.