Filtered Promise#catch

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Filtered Promise#catch

Peter Jaszkowiak
Excuse me if this has been discussed previously, I did try to find existing discussions.

Bluebird has very useful functionality in `Promise.prototype.catch`, which allows for filtering certain error types. Here is an example:

```js
database.get('user:Bob')
  .catch(UserNotFoundError, (err) => {
    console.error('User not found: ', err);
  })
```

Which is a shortcut for the following:

```js
database.get('user:Bob')
  .catch((err) => {
    if (err instanceof UserNotFoundError) {
      console.error('User not found: ', err);
      return;
    }

    throw err;
  })
```

I think this would be a huge improvement to error handling in asynchronous situations, especially since many people dislike using `try { ... } catch (e) { ... }` syntax (which would also benefit from some sort of error filtering).

### Options

I think that passing in the matching argument as the second argument is preferable to passing it as the first argument, but in the spririt of compatibility, supporting it in the Bluebird fashion is best. Alternatively, a new prototype method (like `catchFilter` or `error`) could be used to instead support the same functionality.

Terminology:
- Callback  
  The function passed as the next operation in the chain, to be called if a rejection occurs
- Matcher
  The argument passed to select a certain type of error to be caught, and then execute the provided callback

#### Use `instanceof`
The class or constructor would be passed as the matcher

This on its own is  It would not support instances from other contexts, nor would it support custom errors created without subclassing (like manually setting error.name).

Example
```js
const err = new CustomError();
Promise.reject(err)
  .catch(CustomError, (customError) => {
    // handle CustomError s
  })
  .catch((otherError) => {
    // handle other errors
  });
```

#### Use pattern matching
An object would be passed as the matcher, and it's own enumerable properties would be compared with properties of the error instance. If all properties on the given matcher are strictly equal to the same properties on the error instance, it's a match.

Example:
```js
const err = new Error();
err.name = 'CustomError';
Promise.reject(err)
  .catch({ name: 'CustomError' }, (customError) => {
    // handle CustomError s
  })
  .catch((otherError) => {
    // handle other errors
  });
```

This would allow for matching any error type, and could support a subset of the `instanceof` check by doing something like `{ constructor: CustomError }`.

#### Use a matcher function
A function would be passed as the matcher, receiving the err as its argument. It would then be able to do any operation on the error instance to check if it is the correct error type. 

Example:
```js
const err = new Error();
err.name = 'CustomError';
Promise.reject(err)
  .catch(err => (err.name === 'CustomError'), (customError) => {
    // handle CustomError s
  })
  .catch((otherError) => {
    // handle other errors
  });
```

However, this is almost no different from just including the tests in the catch body itself. It's verbose enough that the benefit of supporting this is not nearly as significant than the other options.

#### Support `instanceof` and pattern matching
In my opinion, this is the best of both worlds. You get to support the basic `instanceof` case with inheritance, etc, while also supporting matching more custom errors made in a less standard manner.

The method would check if the matcher is a function, and if so, it would use `instanceof`. Otherwise, it would treat the argument as an object and compare the properties.


### Example Naive Polyfill

```js
const origCatch = Promise.prototype.catch;
Promise.prototype.catch = { catch (ErrorType, callback) {
  if (typeof callback !== 'function' && typeof ErrorType === 'function') {
    callback = ErrorType;
    ErrorType = null;
  }

  if (!ErrorType || !(typeof ErrorType === 'object' || typeof ErrorType === 'function')) {
    return origCatch.call(this, callback);
  }

  // if the ErrorType is a function, use instanceof
  if (typeof ErrorType === 'function') {
    return origCatch.call(this, (err) => {
      if (err instanceof ErrorType) {
        return callback(err);
      }

      throw err;
    });
  }

  // otherwise use pattern matching
  return origCatch.call(this, (err) => {
    const matches = Object.entries(ErrorType)
      .every(([key, value]) => (err[key] === value));
    if (matches) {
      return callback(err);
    }

    throw err;
  });
} }.catch;
```


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Filtered Promise#catch

Jonathan Barronville
Yes, +1Filtered .catch(…) is a Bluebird feature that I use quite a lot. Native support would be great. I’ve personally only ever needed to use the constructor matcher version (i.e., the version which uses instanceof under the hood), but I can see why support for the other versions (object predicate and function predicate) could be useful.

On Tue, Oct 10, 2017 at 7:06 PM, Peter Jaszkowiak <[hidden email]> wrote:
Excuse me if this has been discussed previously, I did try to find existing discussions.

Bluebird has very useful functionality in `Promise.prototype.catch`, which allows for filtering certain error types. Here is an example:

```js
database.get('user:Bob')
  .catch(UserNotFoundError, (err) => {
    console.error('User not found: ', err);
  })
```

Which is a shortcut for the following:

```js
database.get('user:Bob')
  .catch((err) => {
    if (err instanceof UserNotFoundError) {
      console.error('User not found: ', err);
      return;
    }

    throw err;
  })
```

I think this would be a huge improvement to error handling in asynchronous situations, especially since many people dislike using `try { ... } catch (e) { ... }` syntax (which would also benefit from some sort of error filtering).

### Options

I think that passing in the matching argument as the second argument is preferable to passing it as the first argument, but in the spririt of compatibility, supporting it in the Bluebird fashion is best. Alternatively, a new prototype method (like `catchFilter` or `error`) could be used to instead support the same functionality.

Terminology:
- Callback  
  The function passed as the next operation in the chain, to be called if a rejection occurs
- Matcher
  The argument passed to select a certain type of error to be caught, and then execute the provided callback

#### Use `instanceof`
The class or constructor would be passed as the matcher

This on its own is  It would not support instances from other contexts, nor would it support custom errors created without subclassing (like manually setting error.name).

Example
```js
const err = new CustomError();
Promise.reject(err)
  .catch(CustomError, (customError) => {
    // handle CustomError s
  })
  .catch((otherError) => {
    // handle other errors
  });
```

#### Use pattern matching
An object would be passed as the matcher, and it's own enumerable properties would be compared with properties of the error instance. If all properties on the given matcher are strictly equal to the same properties on the error instance, it's a match.

Example:
```js
const err = new Error();
err.name = 'CustomError';
Promise.reject(err)
  .catch({ name: 'CustomError' }, (customError) => {
    // handle CustomError s
  })
  .catch((otherError) => {
    // handle other errors
  });
```

This would allow for matching any error type, and could support a subset of the `instanceof` check by doing something like `{ constructor: CustomError }`.

#### Use a matcher function
A function would be passed as the matcher, receiving the err as its argument. It would then be able to do any operation on the error instance to check if it is the correct error type. 

Example:
```js
const err = new Error();
err.name = 'CustomError';
Promise.reject(err)
  .catch(err => (err.name === 'CustomError'), (customError) => {
    // handle CustomError s
  })
  .catch((otherError) => {
    // handle other errors
  });
```

However, this is almost no different from just including the tests in the catch body itself. It's verbose enough that the benefit of supporting this is not nearly as significant than the other options.

#### Support `instanceof` and pattern matching
In my opinion, this is the best of both worlds. You get to support the basic `instanceof` case with inheritance, etc, while also supporting matching more custom errors made in a less standard manner.

The method would check if the matcher is a function, and if so, it would use `instanceof`. Otherwise, it would treat the argument as an object and compare the properties.


### Example Naive Polyfill

```js
const origCatch = Promise.prototype.catch;
Promise.prototype.catch = { catch (ErrorType, callback) {
  if (typeof callback !== 'function' && typeof ErrorType === 'function') {
    callback = ErrorType;
    ErrorType = null;
  }

  if (!ErrorType || !(typeof ErrorType === 'object' || typeof ErrorType === 'function')) {
    return origCatch.call(this, callback);
  }

  // if the ErrorType is a function, use instanceof
  if (typeof ErrorType === 'function') {
    return origCatch.call(this, (err) => {
      if (err instanceof ErrorType) {
        return callback(err);
      }

      throw err;
    });
  }

  // otherwise use pattern matching
  return origCatch.call(this, (err) => {
    const matches = Object.entries(ErrorType)
      .every(([key, value]) => (err[key] === value));
    if (matches) {
      return callback(err);
    }

    throw err;
  });
} }.catch;
```


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss




--
- Jonathan


Life is a game and we’re all just high density pixels.

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Christopher Thorn
In reply to this post by Peter Jaszkowiak
I agree that catch guards are useful, but they can be implemented in
userland fairly ergonomically:

```
function guard(predicate, callback) {
  return function guarded(reason) {
    if (!predicate(reason)) {
      throw reason;
    }
    return callback(reason);
  };
}

function instanceOf(constructor, callback) {
  return guard(reason => reason instanceof constructor, callback);
}

Promise.resolve('invalid')
  .then(JSON.parse)
  .catch(instanceOf(SyntaxError, reason => {
    // do something to handle the syntax error, or perhaps just silence it
  }));
```

Regards, Chris
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Peter Jaszkowiak
In reply to this post by Peter Jaszkowiak
True, it's a fairly trivial thing, but so is `Array.prototype.includes`. I can give a hundred other examples. 

There is a reason that so many people use underscore, ramda, lodash, etc: the "standard library" in Javascript is incomplete. We should aim to provide as many of these helpful utilities as possible. Not only does it improve developer experience, it also reduces payloads people need to download over the wire.

I can understand why people are hesitant to add _syntactic additions_ to JavaScript, but when it comes to such useful cases that are already in wide use via libraries, I can't understand the reservations.

You did provide a good method name (`guard`) if the final decision is to avoid overloading `catch` (which I don't mind either way, in some ways it would be preferable so the argument order could be reversed).

Anyways, I think the best implementation would do three things:

1. If `matcher` is a function, check if `matcher.prototype instanceof Error`, if so, use `instanceof` to check if a rejected error matches
2. If `matcher` is a function that doesn't inherit from `Error`, execute it with the rejected error to get the match condition
3. If `matcher` is an object, check every own enumerable property against the rejected error to get the match condition

This seems pretty complicated, in that it doesn't seem to match the principle of avoiding overloading in the standard library. I think in this case, removing any of this functionality is a significant loss, but if any were to be removed, I'd choose #3.

Regards,
Peter

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Jonathan Barronville
__@Christopher Thorn:__ I do see your point, but as pointed out by
__Peter Jaszkowiak__, there are already several little facilities
which have been added to JavaScript that are actually useful but could
relatively easily/straightforwardly be implemented in userland. This
particular feature (*i.e.*, filtered catch) is a very useful one that
I, like __Peter Jaszkowiak__ and likely lots of other folks, find
myself using *a lot*. In fact, there are at least a couple projects
where I've wanted to use Bluebird just due to the usefulness of that
feature alone and how clean it allows my code to be. Of course, we
don't want to add every potentially useful feature to the language,
but I think a case like this is a perfect example of when it makes
sense (*IMHO*).

On Wed, Oct 11, 2017 at 12:10 AM, Peter Jaszkowiak <[hidden email]> wrote:

> True, it's a fairly trivial thing, but so is `Array.prototype.includes`. I
> can give a hundred other examples.
>
> There is a reason that so many people use underscore, ramda, lodash, etc:
> the "standard library" in Javascript is incomplete. We should aim to provide
> as many of these helpful utilities as possible. Not only does it improve
> developer experience, it also reduces payloads people need to download over
> the wire.
>
> I can understand why people are hesitant to add _syntactic additions_ to
> JavaScript, but when it comes to such useful cases that are already in wide
> use via libraries, I can't understand the reservations.
>
> You did provide a good method name (`guard`) if the final decision is to
> avoid overloading `catch` (which I don't mind either way, in some ways it
> would be preferable so the argument order could be reversed).
>
> Anyways, I think the best implementation would do three things:
>
> 1. If `matcher` is a function, check if `matcher.prototype instanceof
> Error`, if so, use `instanceof` to check if a rejected error matches
> 2. If `matcher` is a function that doesn't inherit from `Error`, execute it
> with the rejected error to get the match condition
> 3. If `matcher` is an object, check every own enumerable property against
> the rejected error to get the match condition
>
> This seems pretty complicated, in that it doesn't seem to match the
> principle of avoiding overloading in the standard library. I think in this
> case, removing any of this functionality is a significant loss, but if any
> were to be removed, I'd choose #3.
>
> Regards,
> Peter
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>



--
- Jonathan



Life is a game and we’re all just high density pixels.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Michał Wadas
In reply to this post by Christopher Thorn
I don't get why argument "it's easy to implement in user land" is raised. 

All Array methods are easy to implement in user land. Including recent additions like .includes and ES2015 methods. 

Promises are easy to implement in user land. Smallest implementation is less than 1kB AFAIR. 

Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is present. Probably less than 50 lines of code.

Object.values, Object.entries, Object.assign, Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These methods were added recently but they can be implemented in user land.

On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
I agree that catch guards are useful, but they can be implemented in
userland fairly ergonomically:

```
function guard(predicate, callback) {
  return function guarded(reason) {
    if (!predicate(reason)) {
      throw reason;
    }
    return callback(reason);
  };
}

function instanceOf(constructor, callback) {
  return guard(reason => reason instanceof constructor, callback);
}

Promise.resolve('invalid')
  .then(JSON.parse)
  .catch(instanceOf(SyntaxError, reason => {
    // do something to handle the syntax error, or perhaps just silence it
  }));
```

Regards, Chris
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

T.J. Crowder-2
On Wed, Oct 11, 2017 at 1:28 PM, Michał Wadas <[hidden email]> wrote:
>
> I don't get why argument "it's easy to implement in user land" is raised.
> (etc)

Indeed (with the exception of promises, which couldn't be properly implemented in userland without environment-specific facilities for doing something asynchronously).

Which brings us back to https://esdiscuss.org/topic/guidelines-and-general-direction-for-standard-library-proposals (esdiscuss.org likes to munge that link; here's a smaller version: http://tinyurl.com/ydyu9xtt): Until or unless TC39 thrash out a consensus (I'm told there is none at present) about whether a robust or minimal standard library is the direction to go in future, additions to it will tend to be fairly arbitrary, which isn't a great place to be.

-- T.J. Crowder

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Christopher Thorn
In reply to this post by Michał Wadas
I don't think it's pedantic to point out that you're misstating my
argument. I didn't say "easy", I said "ergonomic", after all.

I've used bluebird quite a lot, and I've also used native promises and
other promise libraries without filtered catch. At first, I missed
this feature from bluebird, but I no longer do.

Personally, I prefer the ergonomics of helper functions like I
demonstrated to an overloaded `catch` method, especially one which
does different things depending on the types of the arguments that are
passed (one behavior for the instanceof check, another for a function
predicate and another for an object predicate). Using an separate
function gives the user a chance to specify exactly the behavior they
want, no more and no less, and gives the user a chance to name the
filter.

Regards,
Chris

On Wed, Oct 11, 2017 at 5:28 AM, Michał Wadas <[hidden email]> wrote:

> I don't get why argument "it's easy to implement in user land" is raised.
>
> All Array methods are easy to implement in user land. Including recent
> additions like .includes and ES2015 methods.
>
> Promises are easy to implement in user land. Smallest implementation is less
> than 1kB AFAIR.
>
> Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is
> present. Probably less than 50 lines of code.
>
> Object.values, Object.entries, Object.assign,
> Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These
> methods were added recently but they can be implemented in user land.
>
> On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
>>
>> I agree that catch guards are useful, but they can be implemented in
>> userland fairly ergonomically:
>>
>> ```
>> function guard(predicate, callback) {
>>   return function guarded(reason) {
>>     if (!predicate(reason)) {
>>       throw reason;
>>     }
>>     return callback(reason);
>>   };
>> }
>>
>> function instanceOf(constructor, callback) {
>>   return guard(reason => reason instanceof constructor, callback);
>> }
>>
>> Promise.resolve('invalid')
>>   .then(JSON.parse)
>>   .catch(instanceOf(SyntaxError, reason => {
>>     // do something to handle the syntax error, or perhaps just silence it
>>   }));
>> ```
>>
>> Regards, Chris
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Peter Jaszkowiak
If you're going to distinguish between "ergonomic" and "easy" in the context of JavaScript development, you're going to have to define what you think the difference is.

For something that should, imo, be used in almost every case that `catch` is used, I don't think that it just being ergonomic to create your own helper functions is enough. Are you going to create your own helper function like that for every error type across every file? 

Do you want the developer to provide their own `guard` etc helper functions like you did? Why should they have to? 

As I said before, I think the criticism of overloading is fine. However, this is something more useful than `Array#includes` and already in use more often than that Array method. 

We can use different method names to separate different types of guards if you feel like it's a preferable idea. However, it definitely needs to be done because there is significant demand demonstrated by many people still using bluebird for these utility functions. 

On Oct 11, 2017 7:06 AM, "Christopher Thorn" <[hidden email]> wrote:
I don't think it's pedantic to point out that you're misstating my
argument. I didn't say "easy", I said "ergonomic", after all.

I've used bluebird quite a lot, and I've also used native promises and
other promise libraries without filtered catch. At first, I missed
this feature from bluebird, but I no longer do.

Personally, I prefer the ergonomics of helper functions like I
demonstrated to an overloaded `catch` method, especially one which
does different things depending on the types of the arguments that are
passed (one behavior for the instanceof check, another for a function
predicate and another for an object predicate). Using an separate
function gives the user a chance to specify exactly the behavior they
want, no more and no less, and gives the user a chance to name the
filter.

Regards,
Chris

On Wed, Oct 11, 2017 at 5:28 AM, Michał Wadas <[hidden email]> wrote:
> I don't get why argument "it's easy to implement in user land" is raised.
>
> All Array methods are easy to implement in user land. Including recent
> additions like .includes and ES2015 methods.
>
> Promises are easy to implement in user land. Smallest implementation is less
> than 1kB AFAIR.
>
> Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is
> present. Probably less than 50 lines of code.
>
> Object.values, Object.entries, Object.assign,
> Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These
> methods were added recently but they can be implemented in user land.
>
> On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
>>
>> I agree that catch guards are useful, but they can be implemented in
>> userland fairly ergonomically:
>>
>> ```
>> function guard(predicate, callback) {
>>   return function guarded(reason) {
>>     if (!predicate(reason)) {
>>       throw reason;
>>     }
>>     return callback(reason);
>>   };
>> }
>>
>> function instanceOf(constructor, callback) {
>>   return guard(reason => reason instanceof constructor, callback);
>> }
>>
>> Promise.resolve('invalid')
>>   .then(JSON.parse)
>>   .catch(instanceOf(SyntaxError, reason => {
>>     // do something to handle the syntax error, or perhaps just silence it
>>   }));
>> ```
>>
>> Regards, Chris
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Christopher Thorn
Inline

> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.

The difference is that it seemed like people thought I meant "this is
easy to implement", but I was talking about the ergonomics of using
the functions once they're defined. Or trying to, at least :)

> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?

In the codebase I am currently working on, I have functions like these
defined in modules, which I import where they are needed.

> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?

They could be defined by a library, but they don't need to be methods
the Promise prototype.

I prefer separate functions because of their explicit intent. In
particular, I'm not a huge fan of bluebird's overloaded `catch`
method. However, if you don't overload it, it seems like you'd need to
specify three methods in addition to catch: `catchInstancesOf`, one
for function predicates, and one for object predicates (I'm not sure
what I'd call those two). Having four methods for handling rejections
is too many.

Maybe functions like the ones I demonstrated could be added to the
standard library (say, as static properties of the Promise object).

Regards,
Chris

On Wed, Oct 11, 2017 at 7:14 AM, Peter Jaszkowiak <[hidden email]> wrote:

> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.
>
> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?
>
> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?
>
> As I said before, I think the criticism of overloading is fine. However,
> this is something more useful than `Array#includes` and already in use more
> often than that Array method.
>
> We can use different method names to separate different types of guards if
> you feel like it's a preferable idea. However, it definitely needs to be
> done because there is significant demand demonstrated by many people still
> using bluebird for these utility functions.
>
> On Oct 11, 2017 7:06 AM, "Christopher Thorn" <[hidden email]> wrote:
>>
>> I don't think it's pedantic to point out that you're misstating my
>> argument. I didn't say "easy", I said "ergonomic", after all.
>>
>> I've used bluebird quite a lot, and I've also used native promises and
>> other promise libraries without filtered catch. At first, I missed
>> this feature from bluebird, but I no longer do.
>>
>> Personally, I prefer the ergonomics of helper functions like I
>> demonstrated to an overloaded `catch` method, especially one which
>> does different things depending on the types of the arguments that are
>> passed (one behavior for the instanceof check, another for a function
>> predicate and another for an object predicate). Using an separate
>> function gives the user a chance to specify exactly the behavior they
>> want, no more and no less, and gives the user a chance to name the
>> filter.
>>
>> Regards,
>> Chris
>>
>> On Wed, Oct 11, 2017 at 5:28 AM, Michał Wadas <[hidden email]>
>> wrote:
>> > I don't get why argument "it's easy to implement in user land" is
>> > raised.
>> >
>> > All Array methods are easy to implement in user land. Including recent
>> > additions like .includes and ES2015 methods.
>> >
>> > Promises are easy to implement in user land. Smallest implementation is
>> > less
>> > than 1kB AFAIR.
>> >
>> > Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is
>> > present. Probably less than 50 lines of code.
>> >
>> > Object.values, Object.entries, Object.assign,
>> > Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These
>> > methods were added recently but they can be implemented in user land.
>> >
>> > On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
>> >>
>> >> I agree that catch guards are useful, but they can be implemented in
>> >> userland fairly ergonomically:
>> >>
>> >> ```
>> >> function guard(predicate, callback) {
>> >>   return function guarded(reason) {
>> >>     if (!predicate(reason)) {
>> >>       throw reason;
>> >>     }
>> >>     return callback(reason);
>> >>   };
>> >> }
>> >>
>> >> function instanceOf(constructor, callback) {
>> >>   return guard(reason => reason instanceof constructor, callback);
>> >> }
>> >>
>> >> Promise.resolve('invalid')
>> >>   .then(JSON.parse)
>> >>   .catch(instanceOf(SyntaxError, reason => {
>> >>     // do something to handle the syntax error, or perhaps just silence
>> >> it
>> >>   }));
>> >> ```
>> >>
>> >> Regards, Chris
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Jordan Harband
Here's why catch guards aren't an easy feature: Promise catch mirrors syntactic catch. Promise .catch will not get any new features that aren't simultaneously available to syntactic catch - full stop.

OK, now you're adding syntax, great! How do you match types? `instanceof` is utterly unreliable, because it doesn't work cross-realm, so a number of us on the committee would block on that.

OK, now you're adding a cross-realm brand-checking mechanism. There's committee members who would block on that.

OK, maybe now you're adding a generic matching protocol, because what if you want users to be able to supply more complex guard checks - oh wait, there's already a proposal for that: https://github.com/tc39/proposal-pattern-matching



Please do not assume that any given fun idea, given all the edge cases, constraints, and interoperability requirements, will be "easy" to add.

On Wed, Oct 11, 2017 at 7:52 AM, Christopher Thorn <[hidden email]> wrote:
Inline

> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.

The difference is that it seemed like people thought I meant "this is
easy to implement", but I was talking about the ergonomics of using
the functions once they're defined. Or trying to, at least :)

> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?

In the codebase I am currently working on, I have functions like these
defined in modules, which I import where they are needed.

> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?

They could be defined by a library, but they don't need to be methods
the Promise prototype.

I prefer separate functions because of their explicit intent. In
particular, I'm not a huge fan of bluebird's overloaded `catch`
method. However, if you don't overload it, it seems like you'd need to
specify three methods in addition to catch: `catchInstancesOf`, one
for function predicates, and one for object predicates (I'm not sure
what I'd call those two). Having four methods for handling rejections
is too many.

Maybe functions like the ones I demonstrated could be added to the
standard library (say, as static properties of the Promise object).

Regards,
Chris

On Wed, Oct 11, 2017 at 7:14 AM, Peter Jaszkowiak <[hidden email]> wrote:
> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.
>
> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?
>
> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?
>
> As I said before, I think the criticism of overloading is fine. However,
> this is something more useful than `Array#includes` and already in use more
> often than that Array method.
>
> We can use different method names to separate different types of guards if
> you feel like it's a preferable idea. However, it definitely needs to be
> done because there is significant demand demonstrated by many people still
> using bluebird for these utility functions.
>
> On Oct 11, 2017 7:06 AM, "Christopher Thorn" <[hidden email]> wrote:
>>
>> I don't think it's pedantic to point out that you're misstating my
>> argument. I didn't say "easy", I said "ergonomic", after all.
>>
>> I've used bluebird quite a lot, and I've also used native promises and
>> other promise libraries without filtered catch. At first, I missed
>> this feature from bluebird, but I no longer do.
>>
>> Personally, I prefer the ergonomics of helper functions like I
>> demonstrated to an overloaded `catch` method, especially one which
>> does different things depending on the types of the arguments that are
>> passed (one behavior for the instanceof check, another for a function
>> predicate and another for an object predicate). Using an separate
>> function gives the user a chance to specify exactly the behavior they
>> want, no more and no less, and gives the user a chance to name the
>> filter.
>>
>> Regards,
>> Chris
>>
>> On Wed, Oct 11, 2017 at 5:28 AM, Michał Wadas <[hidden email]>
>> wrote:
>> > I don't get why argument "it's easy to implement in user land" is
>> > raised.
>> >
>> > All Array methods are easy to implement in user land. Including recent
>> > additions like .includes and ES2015 methods.
>> >
>> > Promises are easy to implement in user land. Smallest implementation is
>> > less
>> > than 1kB AFAIR.
>> >
>> > Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is
>> > present. Probably less than 50 lines of code.
>> >
>> > Object.values, Object.entries, Object.assign,
>> > Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These
>> > methods were added recently but they can be implemented in user land.
>> >
>> > On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
>> >>
>> >> I agree that catch guards are useful, but they can be implemented in
>> >> userland fairly ergonomically:
>> >>
>> >> ```
>> >> function guard(predicate, callback) {
>> >>   return function guarded(reason) {
>> >>     if (!predicate(reason)) {
>> >>       throw reason;
>> >>     }
>> >>     return callback(reason);
>> >>   };
>> >> }
>> >>
>> >> function instanceOf(constructor, callback) {
>> >>   return guard(reason => reason instanceof constructor, callback);
>> >> }
>> >>
>> >> Promise.resolve('invalid')
>> >>   .then(JSON.parse)
>> >>   .catch(instanceOf(SyntaxError, reason => {
>> >>     // do something to handle the syntax error, or perhaps just silence
>> >> it
>> >>   }));
>> >> ```
>> >>
>> >> Regards, Chris
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Re: Filtered Promise#catch

doodad-js Admin

>> OK, now you're adding syntax, great! How do you match types? `instanceof` is utterly unreliable, because it doesn't work cross-realm, so a number of us on the committee would block on that.

>> OK, now you're adding a cross-realm brand-checking mechanism. There's committee members who would block on that.

 

I’ve fixed that (cross-realms) with GUUIDs and a decent type subsystem.

 

 

From: Jordan Harband [mailto:[hidden email]]
Sent: Wednesday, October 11, 2017 4:06 PM
To: Christopher Thorn <[hidden email]>
Cc: es-discuss <[hidden email]>
Subject: Re: Re: Filtered Promise#catch

 

Here's why catch guards aren't an easy feature: Promise catch mirrors syntactic catch. Promise .catch will not get any new features that aren't simultaneously available to syntactic catch - full stop.

 

OK, now you're adding syntax, great! How do you match types? `instanceof` is utterly unreliable, because it doesn't work cross-realm, so a number of us on the committee would block on that.

 

OK, now you're adding a cross-realm brand-checking mechanism. There's committee members who would block on that.

 

OK, maybe now you're adding a generic matching protocol, because what if you want users to be able to supply more complex guard checks - oh wait, there's already a proposal for that: https://github.com/tc39/proposal-pattern-matching

 

 

 

Please do not assume that any given fun idea, given all the edge cases, constraints, and interoperability requirements, will be "easy" to add.

 

On Wed, Oct 11, 2017 at 7:52 AM, Christopher Thorn <[hidden email]> wrote:

Inline

> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.

The difference is that it seemed like people thought I meant "this is
easy to implement", but I was talking about the ergonomics of using
the functions once they're defined. Or trying to, at least :)

> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?

In the codebase I am currently working on, I have functions like these
defined in modules, which I import where they are needed.

> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?

They could be defined by a library, but they don't need to be methods
the Promise prototype.

I prefer separate functions because of their explicit intent. In
particular, I'm not a huge fan of bluebird's overloaded `catch`
method. However, if you don't overload it, it seems like you'd need to
specify three methods in addition to catch: `catchInstancesOf`, one
for function predicates, and one for object predicates (I'm not sure
what I'd call those two). Having four methods for handling rejections
is too many.

Maybe functions like the ones I demonstrated could be added to the
standard library (say, as static properties of the Promise object).

Regards,
Chris


On Wed, Oct 11, 2017 at 7:14 AM, Peter Jaszkowiak <[hidden email]> wrote:


> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.
>
> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?
>
> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?
>
> As I said before, I think the criticism of overloading is fine. However,
> this is something more useful than `Array#includes` and already in use more
> often than that Array method.
>
> We can use different method names to separate different types of guards if
> you feel like it's a preferable idea. However, it definitely needs to be
> done because there is significant demand demonstrated by many people still
> using bluebird for these utility functions.
>
> On Oct 11, 2017 7:06 AM, "Christopher Thorn" <[hidden email]> wrote:
>>
>> I don't think it's pedantic to point out that you're misstating my
>> argument. I didn't say "easy", I said "ergonomic", after all.
>>
>> I've used bluebird quite a lot, and I've also used native promises and
>> other promise libraries without filtered catch. At first, I missed
>> this feature from bluebird, but I no longer do.
>>
>> Personally, I prefer the ergonomics of helper functions like I
>> demonstrated to an overloaded `catch` method, especially one which
>> does different things depending on the types of the arguments that are
>> passed (one behavior for the instanceof check, another for a function
>> predicate and another for an object predicate). Using an separate
>> function gives the user a chance to specify exactly the behavior they
>> want, no more and no less, and gives the user a chance to name the
>> filter.
>>
>> Regards,
>> Chris
>>
>> On Wed, Oct 11, 2017 at 5:28 AM, Michał Wadas <[hidden email]>
>> wrote:
>> > I don't get why argument "it's easy to implement in user land" is
>> > raised.
>> >
>> > All Array methods are easy to implement in user land. Including recent
>> > additions like .includes and ES2015 methods.
>> >
>> > Promises are easy to implement in user land. Smallest implementation is
>> > less
>> > than 1kB AFAIR.
>> >
>> > Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is
>> > present. Probably less than 50 lines of code.
>> >
>> > Object.values, Object.entries, Object.assign,
>> > Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These
>> > methods were added recently but they can be implemented in user land.
>> >
>> > On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
>> >>
>> >> I agree that catch guards are useful, but they can be implemented in
>> >> userland fairly ergonomically:
>> >>
>> >> ```
>> >> function guard(predicate, callback) {
>> >>   return function guarded(reason) {
>> >>     if (!predicate(reason)) {
>> >>       throw reason;
>> >>     }
>> >>     return callback(reason);
>> >>   };
>> >> }
>> >>
>> >> function instanceOf(constructor, callback) {
>> >>   return guard(reason => reason instanceof constructor, callback);
>> >> }
>> >>
>> >> Promise.resolve('invalid')
>> >>   .then(JSON.parse)
>> >>   .catch(instanceOf(SyntaxError, reason => {
>> >>     // do something to handle the syntax error, or perhaps just silence
>> >> it
>> >>   }));
>> >> ```
>> >>
>> >> Regards, Chris
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

 


Virus-free. www.avg.com

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Jordan Harband
GUUIDs only work if they're deterministically generate-able in each realm; which isn't something that typically can be performantly and robustly done. If you've got such a solution, please ping me directly (no need to pollute the list) as I'd love to look into it!

On Wed, Oct 11, 2017 at 3:16 PM, doodad-js Admin <[hidden email]> wrote:

>> OK, now you're adding syntax, great! How do you match types? `instanceof` is utterly unreliable, because it doesn't work cross-realm, so a number of us on the committee would block on that.

>> OK, now you're adding a cross-realm brand-checking mechanism. There's committee members who would block on that.

 

I’ve fixed that (cross-realms) with GUUIDs and a decent type subsystem.

 

 

From: Jordan Harband [mailto:[hidden email]]
Sent: Wednesday, October 11, 2017 4:06 PM
To: Christopher Thorn <[hidden email]>
Cc: es-discuss <[hidden email]>
Subject: Re: Re: Filtered Promise#catch

 

Here's why catch guards aren't an easy feature: Promise catch mirrors syntactic catch. Promise .catch will not get any new features that aren't simultaneously available to syntactic catch - full stop.

 

OK, now you're adding syntax, great! How do you match types? `instanceof` is utterly unreliable, because it doesn't work cross-realm, so a number of us on the committee would block on that.

 

OK, now you're adding a cross-realm brand-checking mechanism. There's committee members who would block on that.

 

OK, maybe now you're adding a generic matching protocol, because what if you want users to be able to supply more complex guard checks - oh wait, there's already a proposal for that: https://github.com/tc39/proposal-pattern-matching

 

 

 

Please do not assume that any given fun idea, given all the edge cases, constraints, and interoperability requirements, will be "easy" to add.

 

On Wed, Oct 11, 2017 at 7:52 AM, Christopher Thorn <[hidden email]> wrote:

Inline

> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.

The difference is that it seemed like people thought I meant "this is
easy to implement", but I was talking about the ergonomics of using
the functions once they're defined. Or trying to, at least :)

> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?

In the codebase I am currently working on, I have functions like these
defined in modules, which I import where they are needed.

> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?

They could be defined by a library, but they don't need to be methods
the Promise prototype.

I prefer separate functions because of their explicit intent. In
particular, I'm not a huge fan of bluebird's overloaded `catch`
method. However, if you don't overload it, it seems like you'd need to
specify three methods in addition to catch: `catchInstancesOf`, one
for function predicates, and one for object predicates (I'm not sure
what I'd call those two). Having four methods for handling rejections
is too many.

Maybe functions like the ones I demonstrated could be added to the
standard library (say, as static properties of the Promise object).

Regards,
Chris


On Wed, Oct 11, 2017 at 7:14 AM, Peter Jaszkowiak <[hidden email]> wrote:


> If you're going to distinguish between "ergonomic" and "easy" in the context
> of JavaScript development, you're going to have to define what you think the
> difference is.
>
> For something that should, imo, be used in almost every case that `catch` is
> used, I don't think that it just being ergonomic to create your own helper
> functions is enough. Are you going to create your own helper function like
> that for every error type across every file?
>
> Do you want the developer to provide their own `guard` etc helper functions
> like you did? Why should they have to?
>
> As I said before, I think the criticism of overloading is fine. However,
> this is something more useful than `Array#includes` and already in use more
> often than that Array method.
>
> We can use different method names to separate different types of guards if
> you feel like it's a preferable idea. However, it definitely needs to be
> done because there is significant demand demonstrated by many people still
> using bluebird for these utility functions.
>
> On Oct 11, 2017 7:06 AM, "Christopher Thorn" <[hidden email]> wrote:
>>
>> I don't think it's pedantic to point out that you're misstating my
>> argument. I didn't say "easy", I said "ergonomic", after all.
>>
>> I've used bluebird quite a lot, and I've also used native promises and
>> other promise libraries without filtered catch. At first, I missed
>> this feature from bluebird, but I no longer do.
>>
>> Personally, I prefer the ergonomics of helper functions like I
>> demonstrated to an overloaded `catch` method, especially one which
>> does different things depending on the types of the arguments that are
>> passed (one behavior for the instanceof check, another for a function
>> predicate and another for an object predicate). Using an separate
>> function gives the user a chance to specify exactly the behavior they
>> want, no more and no less, and gives the user a chance to name the
>> filter.
>>
>> Regards,
>> Chris
>>
>> On Wed, Oct 11, 2017 at 5:28 AM, Michał Wadas <[hidden email]>
>> wrote:
>> > I don't get why argument "it's easy to implement in user land" is
>> > raised.
>> >
>> > All Array methods are easy to implement in user land. Including recent
>> > additions like .includes and ES2015 methods.
>> >
>> > Promises are easy to implement in user land. Smallest implementation is
>> > less
>> > than 1kB AFAIR.
>> >
>> > Maps, Sets, WeakSets are trivial to implement in user land if WeakMap is
>> > present. Probably less than 50 lines of code.
>> >
>> > Object.values, Object.entries, Object.assign,
>> > Object.getOwnPropertyDescriptors, Object.is, Array.from, Array.of. These
>> > methods were added recently but they can be implemented in user land.
>> >
>> > On 11 Oct 2017 2:28 am, "Christopher Thorn" <[hidden email]> wrote:
>> >>
>> >> I agree that catch guards are useful, but they can be implemented in
>> >> userland fairly ergonomically:
>> >>
>> >> ```
>> >> function guard(predicate, callback) {
>> >>   return function guarded(reason) {
>> >>     if (!predicate(reason)) {
>> >>       throw reason;
>> >>     }
>> >>     return callback(reason);
>> >>   };
>> >> }
>> >>
>> >> function instanceOf(constructor, callback) {
>> >>   return guard(reason => reason instanceof constructor, callback);
>> >> }
>> >>
>> >> Promise.resolve('invalid')
>> >>   .then(JSON.parse)
>> >>   .catch(instanceOf(SyntaxError, reason => {
>> >>     // do something to handle the syntax error, or perhaps just silence
>> >> it
>> >>   }));
>> >> ```
>> >>
>> >> Regards, Chris
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

 


Virus-free. www.avg.com


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Filtered Promise#catch

Michał Wadas
In reply to this post by Jordan Harband
It's possible to standardize already existing filtered catch from SpiderMonkey.

try {
   throw new Error('42');
} catch(e if e.message === 42) {

}

Then Promise.prototype.catch extension can work in very similar manner - by predicate function and user code can easily adopt pattern-matching in future.

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss