Code Monkey home page Code Monkey logo

purify's People

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

purify's Issues

Is there a roadmap?

I was wondering if there was a roadmap, even if vague and/or previously unwritten, for this library?

The API and explanations in the docs so far look great so far. Even though I am not currently using TypeScript, this library seems like one I would adopt (in the untyped JS world, I have adopted Sanctuary).

However, the following is unclear:

  • What are the future goals of the library?
  • Is this library considered finished?
  • Is this library intended to be part of a ecosystem of likeminded libraries or handle additional functionality itself?
  • What should future contributors focus on?

But by all means, if you are satisfied with the current state of the library, that's cool too - I can see a lot of the essentials are already in place. All I can think of are things like typesafe pipes (which I hear are a challenge in TypeScript, and safe record property access, and that's more niche and perhaps better served in a standalone JSON handling library).

(also, thank you for writing this library. It got me interested in fluent functional APIs again!)

discussion: declaratively unwrap Either<L, Promise<R>> into EitherAsync<L, R>?

It'd be really nice if in my beautiful declarative fluent call chain, i could unwrap a Either<L, Promise<R>> into a EitherAsync<L, R> (or just as well a Maybe<Promise<T>> into a MaybeAsync<T>)

If I have:

function saveEgg(egg: Egg): Promise<Egg> { ... }
function breakEgg(egg: Egg): BrokenEgg { ... }

const egg = new Egg();

and my requirements are to save (asynchronous) and then break this egg, and I would like to have an EitherAsync<Error, Egg> at the end, here's how I picture this happening in an ideal world:

const savedEgg = Either.of<Error, Egg>(egg)  // Either<Error, Egg>
                       .map(saveEgg)         // Either<Error, Promise<Egg>>
                       .unwrapAsync()        // EitherAsync<Error, Egg>
                       .map(breakEgg);       // EitherAsync<Error, BrokenEgg>

right now I can kind of do this with .either and a couple helper functions:

function eitherAsyncFromLeft<L>(value: L): EitherAsync<L, never>;
function eitherAsyncFromPromise<T>(promise: Promise<T>): EitherAsync<Error, T>

const savedEgg = Either.of<Error, Egg>(egg) // Either<Error, Egg>
                       .map(saveEgg)        // Either<Error, Promise<Egg>>
                       .either(
                           eitherAsyncFromLeft,
                           eitherAsyncFromPromise,
                       )                    // EitherAsync<Error, Egg>
                       .map(breakEgg);      // EitherAsync<Error, BrokenEgg>

Returning Nothing within Maybe<T> function in strict mode

I have a function with a return type of Maybe<EthereumCredential>. When I return Nothing, TypeScript complains.

However, when I comment out strict: true in my tsconfig, then it compiles fine. Specifically, it looks like the issue is caused by setting strictFunctionTypes: true (which strict enables).

Since many projects use strict mode, is it possible to make it compatible/is there a workaround?

Here's the full output:

[ts]
Type 'Maybe<never>' is not assignable to type 'Maybe<EthereumCredential>'.
  Types of property ''fantasy-land/alt'' are incompatible.
    Type '(other: Maybe<never>) => Maybe<never>' is not assignable to type '(other: Maybe<EthereumCredential>) => Maybe<EthereumCredential>'.
      Types of parameters 'other' and 'other' are incompatible.
        Type 'Maybe<EthereumCredential>' is not assignable to type 'Maybe<never>'.
          Types of property ''fantasy-land/ap'' are incompatible.
            Type '<U>(maybeF: Maybe<(value: EthereumCredential) => U>) => Maybe<U>' is not assignable to type '<U>(maybeF: Maybe<(value: never) => U>) => Maybe<U>'.
              Types of parameters 'maybeF' and 'maybeF' are incompatible.
                Type 'Maybe<(value: never) => any>' is not assignable to type 'Maybe<(value: EthereumCredential) => any>'.
                  Types of property ''fantasy-land/alt'' are incompatible.
                    Type '(other: Maybe<(value: never) => any>) => Maybe<(value: never) => any>' is not assignable to type '(other: Maybe<(value: EthereumCredential) => any>) => Maybe<(value: EthereumCredential) => any>'.
                      Types of parameters 'other' and 'other' are incompatible.
                        Type 'Maybe<(value: EthereumCredential) => any>' is not assignable to type 'Maybe<(value: never) => any>'.
                          Types of property 'orDefault' are incompatible.
                            Type '(defaultValue: (value: EthereumCredential) => any) => (value: EthereumCredential) => any' is not assignable to type '(defaultValue: (value: never) => any) => (value: never) => any'.
                              Types of parameters 'defaultValue' and 'defaultValue' are incompatible.
                                Types of parameters 'value' and 'value' are incompatible.
                                  Type 'EthereumCredential' is not assignable to type 'never'. [2322]

Tuple destructures into union of fst & snd

TS: 3.3.3333
purify: 0.12.2

According to the docs, it should be possible to destructure a tuple with each side retaining its correct type. It's typing as a union of fst and snd instead.

Repro:

import { Tuple } from 'purify-ts/Tuple';

const [a, b] = Tuple(1, 'hello');
// Expected: [number, string]
// Actual: [string | number, string | number]

single entry point for imports?

Hi ๐Ÿ‘‹ love this repo. I'm writing this issue more to start a conversation and ask if this style of import structure was considered:

import { Either, Just, MaybeAsync } from 'purify';
...

vs the current pattern of:

import { Either } from 'purify-ts/Either';
import { Just } from 'purify-ts/Maybe';
import { MaybeAsync } from 'purify-ts/MaybeAsync';
...

It feels a little counter to other npm libraries that have homogeneous import structures, but if there's a good reason for the current style i'll shut up

Question - Codec decoding with multiple errors

Hi,

Thanks for your work on this library! I'm really enjoying using it.

Is there a way to get multiple errors from a decode result?

Example

// codec
const BlogPostCodec = Codec.interface({
  name: string,
  authorId: number
});

I'm trying to build an object like this:

{
  name: "must be a string",
  authorId: "must be a number"
}

[feature] Either#toList()

Much like Maybe#toList(), but always returns fixed array, e.g.:

const [ left = 0, right ] = Right(42).toList();
const [ err, data ] = getEither().toList();

if (err == null) {
    console.info(data);
}

How does it looks like?

Maybe interopability with Tuple

Hey! So, it's only been a few hours but I love this project.

One pain point for me so far has been how to handle a tuple of maybes. For example, imagine a function that returns an object with optional properties. I'm interested in two of these properties. What I'd ideally like in my mind is to be able to form a sort of MaybeTuple.fromNullable, or something along those lines. Instead I ended up with the following which works but feels quite unergonomic:

maybeThing
    .chain(thing => thing.propA && thing.propB
        ? Just(Tuple(thing.propA, thing.propB))
        : Nothing,
    )
    .ifJust(([first, second]) => {
        doSomething(first, second);
    });

In my mind I'm thinking something like this for the chain:

    .chain(thing => MaybeTuple.fromNullable(thing.propA, thing.propB))

Which would return MaybeTuple<typeof thing.propA, typeof thing.propB>, basically a melding of Maybe and Tuple. It's only Just if both arguments pass the not nullable check.

Thoughts?

Another combinator for monad transformer composition

Hello!

There is a common use case when you need to compose EitherAsync with synchronous Either function:

function validate(doc: Document): Either<Error, ValidatedDocument> {
  // ...
}

function fetchDocument(id: string): EitherAsync<Error, Document> {
  // ...
}

fetchDocument("123").chain(doc => liftEither(validate(doc)))

It would be nice to have combinator which will lift Either itself. cats library has subflatMap method for this purpose.

Possible API:

subchain<R2>(f: (value: R) => Either<L, R2>): EitherAsync<L, R2>

// ========

fetchDocument("123").subchain(doc => validate(doc))

What do you think?

Produce es2018 target as additional aside with es5 to NPM

Targeting down to es5 right now is indeed allowing old system to benefit from the module, in additional to that, could be great to have es2018 without downlevelIteration flavor placed so that lots of modern users could opt-in as needed?

I've tested a little bit on local with small changing in build steps, wondering would that be OK to proceed with PR to make the cut?

are Either types correct?

For some reason, I cannot chain left to right and left to left (type errors).

const r1 = () => right(1);
const l1 = () => left('woops');
const l2 = () => left(/woo/);
const rl = () => Math.random() > 0.5 ? left('woo') : right('yay'); // could be the case.

r1().chain(l1); // error
l1().chain(l2); // error

Any thoughts?

I want to use a chain that would change not only the value but also the type of error

Now the function chain is useless if I want to return Either in chain, it more likely will have a different type of error
Example

this.phoneService.findPhone(createPhoneDto).chain(phone => {
      return this.passwordService.checkPhonePassword(phone, createPhoneDto);
})
this.phoneService.findPhone() // => EitherAsync<PhoneNotFound, Phone>
this.passwordService.checkPhonePassword() // => EitherAsync<NoPhonePassword, boolean>

I would like that would be after the call cahin
EitherAsync<PhoneNotFound | NoPhonePassword, boolean>
type
Or tell me how can I do it differently

Use of Maybe as a return type - Nothing complains

Hi,

The function below, which is expected to return a Maybe cannot return Nothing. I am getting the error: Type 'Card' is not assignable to type 'never'

Both returning Maybe.empty() and returning Nothing result with the same error.

What am I doing wrong here?

import { Maybe, Just, Nothing } from "../../functions/src/common/model/utility"
import { Card } from "../../functions/src/common/model/authentication";

export function getCardFromLocaleStorage(): Maybe<Card> {
    var cardJSON = localStorage.getItem("guestCard") || localStorage.getItem("userCard");
    if (!cardJSON) return Maybe.empty();
    try {
        return Just(JSON.parse(cardJSON) as Card)
    }
    catch (err) {
        return Nothing
    }
}

Maybe should be parametric

The maybe implementation does not satisfy the monad laws because the implementation is not parametric in the wrapped value. Example tests that should pass:

test('parametricity', () => {
  expect(Just(undefined).isNothing()).toEqual(false)
  expect(Just(null).isNothing()).toEqual(false)
})

test('Left Id', () => {
  expect(Just(undefined).chain(x => Just(typeof x === 'undefined'))).toEqual((x => Just(typeof x === 'undefined'))(undefined))
  expect(Just(null).chain(x => Just(typeof x === 'object'))).toEqual((x => Just(typeof x === 'object'))(null))
})

Codec returned from Codec.interface requires field value

I want to declare interface with optional field.
So I usenullable with bit change: add undefinedType

function nullable<T>(codec: Codec<T>): Codec<T | null | undefined> {
  return oneOf([codec, nullType, undefinedType]);
}

then declare interface and object value.

const MyObject = Codec.interface({ id: string, name: nullable(string) });
type MyObject = GetInterface<typeof MyObject>;

// Property 'name' is missing in type '{ id: string; }' but required in type '{ id: string; name: string | null | undefined; }'.
// const obj1: MyObject = { id: "1" };
const obj2: MyObject = { id: "1", name: undefined };

obj1 causes compile error because field of name must have a value even if type of value is undefined so, I have to declare undefined explicitly ๐Ÿ˜ข

Another problem,
MyObject Codec can't decode obj1 as MyObject.

// {
//   __value: 'Problem with property "name": it does not exist in received object {"id":"1"}'
// }
console.log(MyObject.decode({ id: "1" }));

This behavior is clumsy to use.
(example usecase: api request validation with optional parameter.)

full example

Add `Either#chainLeft`

Hi there,

great project! I hardly go a day without using purify-ts. A thousand thanks for your work to date!

I'm opening this issue as a place to discuss the addition of a chainLeft function for the Either type, which is like chain but for the rejection branch. I'd define the function as follows

Either a b ~> (a -> Either c b) -> Either c b

so that Right values are kept the same and Left values are chained on the provided function. While chainLeft doesn't appear in the fantasy-land spec, I've found a function like this helpful in "resetting" a computation back on to the successful path, in certain situations from which an error can be recovered (but without losing the surrounding type as with orDefault).

This signature represents one take, I guess after thinking about it there could be other signatures, such as

Either a b ~> (a -> Either c d) -> Either c b|d

in the event that the function provided to chainLeft returns a different type than b.

Nothing concrete for now obviously, just wanted to touch base.

Cheers

Extracting value from either

Hi,

Is there a way to extract value from the Either? (Both Left/Right)

I know should better not extract it but my server returns me an either, and now I need to return a simple json. how can I do the transformation? Through closure? That sounds very ugly

Docs contain some typos.

While sitting on public transport this morning I was informed about your awesome project by a German news site for techies.
Checking out the documentation I stumbled upon some minor typos.
Gonna submit a PR soon...

Chain with async function

Does chain accept an async function? I didn't understand if my code is broken or that chain does not accept async function..

The code that I want to improve via chain is as below. As far as I understand, I should use chain instead of map to eliminate the extract() function all over the code

    var either = await service.form.getFormInstance(req.body.formInstanceId)
    return either
        .map(async (doc: Doc<FormInstance>) => {
            var either = await getTokenObjectValidForFormInstance(req.body.token, doc.data)
            return either
                .map((tokenObject: OrganizationTokenObject | RoomTokenObject) => {
                    return OK(res, bZetOK<FormInstance>(doc.data))
                }).extract() // ???
        })
        .mapLeft((err: ZetError) => ERROR(res, err))
        .extract()

Issues extending base type Maybe<never>

The following issues popped up after upgrading from 0.12.x to 0.13.x:

Property 'extract' in type 'Nothing' is not assignable to the same property in base type 'Maybe'.

  Type '() => this extends AlwaysJust ? never : undefined' is not assignable to type '() => this extends AlwaysJust ? never : never'.
    Type 'this extends AlwaysJust ? never : undefined' is not assignable to type 'this extends AlwaysJust ? never : never'.
      Type 'undefined' is not assignable to type 'never'.

Property 'extractNullable' in type 'Nothing' is not assignable to the same property in base type 'Maybe'.

Type '() => this extends AlwaysJust ? never : null' is not assignable to type '() => this extends AlwaysJust ? never : never'.
  Type 'this extends AlwaysJust ? never : null' is not assignable to type 'this extends AlwaysJust ? never : never'.
    Type 'null' is not assignable to type 'never'.

Improve toString for debugging

Hi, I'm trying out your library, and playing around with it I'm having difficulty debugging with logs. Take for example an Either of value Right({foo: 'bar', bar: { baz: 'baz'}}). When this is stringified it comes out as Right([object Object]) which is quite useless. It would be nice if it can rather JSON stringify that. For example currently I'm doing

Object.prototype.toString = function() {
    return JSON.stringify(this, null, 2)
}
console.log(`${Right({foo: 'bar', bar: { baz: 'baz'}})}`)

which prints:

Right({
  "foo": "bar",
  "bar": {
    "baz": "baz"
  }
})

I don't want to override Object.prototype.toString so it would be awesome if this library can be improved in this way.

Update site to Gatsby 2

Currently it crashes when starting yarn develop (which is just an alias to gatsby develop) with the new version of Gatsby.

To get started you need to:

  • cd into the site folder in this project
  • make sure everything builds correctly by running yarn develop
  • update the Gatsby dependency with yarn

Proposition of a method `toEitherAsyncSwap` on maybeAsync

First, thank you first this great library !

When we want to check the existence of something before going on with the program, we can use MaybeAsync and then .toEitherAsync

I found myself with the opposite case. I want to check that something is not here before going on. Consider the following :

type Person = { id: number };

function findPerson(id: number): MaybeAsync<Person>;
function save(person: Person): EitherAsync<Error, Person>;

const create = (person: Person): EitherAsync<Error, Person> => {
  const maybeAsyncPerson = findPerson(person.id);

  return maybeAsyncPerson
    .toEitherAsyncSwaped(new Error("Cannot create Person which already exists"))
    .chain(() => save(person));
};

I would be happy to make a PR with this new method toEitherAsyncSwaped if you find it interesting.

We can also imagine a swap on eitherAsync, which would be more generic. Then the previous case would become :

const create = (person: Person): EitherAsync<Error, Person> => {
  const maybeAsyncPerson = findPerson(person.id);

  return maybeAsyncPerson
    .toEitherAsync(new Error("Cannot create Person which already exists"))
    .swap()
    .chain(() => save(person));
}

Maybe there is a different way that I did not think of...

isJust/isNothing narrowing

Prior to using this library, my primary means of simulating a Maybe ADT was to make something Nullable (T | null), and treat null as Nothing. In doing so, the following code was possible:

const genNullableNum = () => Math.random() > .5 ? Math.random() : null;
const numberPlease = (a: number) => {};

function example() {
    const nullableNum = genNullableNum();

    if (nullableNum === null) return;

    numberPlease(nullableNum);
}

This code uses early termination to narrow the type of nullableNum to number; if you remove that line, the compiler will correctly complain that you're potentially passing null to numberPlease.

This doesn't appear to be possible with this library, though it's possible I'm misusing it. If you attempt to run similar code using maybeNum.isNothing() in the if statement, the type isn't narrowed. Equally, if you run if (maybeNum.isJust()) the type isn't narrowed inside the if statement either.

The type does narrow if you run Maybe.ifJust or Maybe.isNothing, however due to that using a callback that doesn't permit the early termination pattern I prefer.

Cheers!

Make more methods generic in their argument

It would be nice if some methods would be generic on the argument type. For example currently Maybe<T>::alt takes and returns a Maybe<T>. This makes it impossible to do something like just(5).alt(just('different type')). It would be nice if alt accepted a Maybe<U> and returned a Maybe<T | U> (or even this extends AlwaysJust ? Maybe<T> : Maybe<T | U>). I think this change can be made without breaking backwards compatibility, as T | T is equal to T.

Async Await & Extracting Left if isLeft()

Hi, I just try to plug the library into my project. I have 2 problems which I may wrongly approach but I could not find a solution within the library.

  • How do we handle async/await inside chain or map in Either/Maybe
  • How do we extract Left value from either if it is Left without a default (isLeft() return true)
  • How can I transform an Either<L1,R1> to Either<L1, R2>

my code below is an example for presenting the problems:

having:
async function getDocsByQuery<T>(query: Query): Promise<Util.Either<Util.ZetError, Doc<T>[]>> {..}
async function add(collectionRef: CollectionReference, data: any): Promise<Util.Either<Util.ZetError, string>> {..}
---
export const newUser = async function(organizationId: string, username: string, password: string, active: boolean, 
  displayName?: string, email?: string, tags?: Util.ZetTags, roles?: RoleSet): Promise<Util.Either<Util.ZetError, string>> {
  var user = bOrganizationUserData(username, password, active, displayName, email, tags, roles);
  var collection = db
    .collection("organization")
    .doc(organizationId)
    .collection("user");

  var query = collection
  .where("username", "==", username)
  .where("deleted", "==", false)
  .limit(1)
  var eitherDocs = await getDocsByQuery<OrganizationUserData>(query)
  if(eitherDocs.isRight()){
    // the first query resulted successfuly, I check how many docs returned. if no document, I can add user
    if(eitherDocs.unsafeCoerce().length==0){
      var eitherAdd = await add(collection, user)
      return eitherAdd
    }
    else{
      // there is already a doc with same username, return error
      return Util.Left(bZetError("Username already exists"))
    }    
  }
  else return Util.Left(eitherDocs.leftOrDefault(Util.bZetError("")))
   /// I need to transform the either to comply with the return type
};

What I try to do, is

  • I query users from the db.
  • If no error, I check how many result documents returned. If zero, I (async) add the users and return the either.
  • If error, I create a new Left because the Either that getDocsByQuery returns does not comply with the return type

Add Either API methods to EitherAsync

Will resolve to a promise.

EDIT:

alt
(other: EitherAsync<L, R>): EitherAsync<L, R>

orDefault
(defaultValue: R): Promise<R>

leftOrDefault
(defaultValue: L): Promise<L>

Add Either#swap()

Hi,

I would like to talk about adding the Either type a swap function.

If this is a Left, then return the left value in Right or vice versa: e.g.

const sample: Either<string, number> = getEither();

const swapSample: Either<number, string>= sample.swap();

Specifically, it is the correct behavior that an error always occurs, and it is illegal when no error occurs.

const sample: Maybe<string> = getMaybe();

// In some cases, it is reversed from option. But, there is no `toLeft` instance method for the `Maybe` type.
sample.toLeft(1) // Either<string, number>

// `swap` method can reduce redundancy to some extent.
sample.toEither(1).swap() // Either<string, number>

Workaround

I know that we can use Either#either() for an alternative.

const sample: Either<string, number> = getEither();

const swapSample: Either<number, string> = sample.either(v => Right(v), v => Left(v));
const sample: Maybe<string> = getMaybe();

sample.toEither(1).either(v => Right(v), v => Left(v))

Using purify with Promises

Hi! Thanks for this fantastic library, it's really helpful and the API is very easy to work with. I was wondering if there are any plans to be able to wrap promises in the api?

Add Maybe#maybe

In the case of listToMaybe, maybe make a NonEmpty ADT instead?

Have the exports changed in v15?

I just installed ^0.15.1 to my project and copied this line

import { EitherAsync, liftEither, fromPromise } from 'purify-ts/EitherAsync'

and vscode becomes quite unhappy

image

Did I miss something?

Are you using purify in production?

If yes, please post your company name, logo and whatever other resources you want to share (articles, repos etc) and I'll add them to the landing page of the website ๐Ÿ˜Š

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.