zenparsing / es-typed-catch

Catch anything you like!
10 stars 0 forks source link

Use a syntax that can be further expanded into a full-fledged pattern matching syntax #4

Closed gsklee closed 4 years ago

gsklee commented 8 years ago

Please, do choose a syntax that can be further expanded into a full-fledged pattern matching syntax so we won't regret it again in some distant future.

We've already made similar mistakes; for example, both Destructuring and Module Import share certain identical aspect, but with the now slight variation in syntax, we've lost the ability to explain and further expand the idea using a unified set of vocabulary. This goes directly against the ideal of the Extensible Web Manifesto which vowed to explain features using components as low-level and extensible as possible.

I'd suggest you to check out how Scala is handling this, and a relevant thread on es-discuss:

http://alvinalexander.com/scala/scala-try-catch-finally-syntax-examples-exceptions-wildcard https://kerflyn.wordpress.com/2011/02/14/playing-with-scalas-pattern-matching/ https://esdiscuss.org/topic/error-type-specific-try-catch-blocks#content-11

To be clear, I'm not saying that we need a full-fledged pattern matching proposal in place to pull this off. What I am suggesting is that, the Typed Catch syntax should be designed in a way that when, say, 5 years from now on and we finally decide to add Pattern Matching into ECMAScript, the Typed Catch syntax can be fully explained by Pattern Matching syntax. I think Scala is doing this pretty well and that's why I implore you to take a further look.

zenparsing commented 8 years ago

Thanks for jumping in @gsklee !

We've already made similar mistakes; for example, both Destructuring and Module Import share certain identical aspect, but with the now slight variation in syntax, we've lost the ability to explain and further expand the idea using a unified set of vocabulary.

Sidebar: the difference between destructuring syntax and import syntax was deliberate. The modules proposal actually started off with the same surface syntax, but it was agreed that surface similarities created more problems than it solved.

Can you provide a hypothetical example of a javascript user using a pattern matching catch clause? Seeing use cases would help.

zenparsing commented 8 years ago

Can you provide a hypothetical example of a javascript user using a pattern matching catch clause? Seeing use cases would help

To clarify, I mean an example where the user is using pattern matching features other than "type matching".

gsklee commented 8 years ago

Can you provide a hypothetical example of a javascript user using a pattern matching catch clause? Seeing use cases would help. To clarify, I mean an example where the user is using pattern matching features other than "type matching".

Unfortunately I can't, but that is not the point I was trying to make. What I was suggesting is that perhaps you should consider a Typed Catch syntax that looks like:

try {
  ...
} catch (error) {
  case _: TypeError
  case _: SyntaxError
    ...
    break;

  default:
    ...
}

/* OR */

try {
  ...
} catch (error) match {
  case _: TypeError => ...
  case _: SyntaxError => ...
  case _ => ...
}

So that in the future if we ever add in Pattern Matching, the syntax of Pattern Matching can expand upon the syntax of Typed Catch as well as explaining it:

/* In a future far, far away... Perhaps we have Pattern Matching like this */

switch (expression) match {
  case 1 => ...
  case _: boolean => ...
  case _ => ...
}

function (x) match {
  case 1 => ...
  case _: boolean => ...
  case _ => ...
}

Please note that the syntax is just hypothetical and for illustration purpose only.

Point is, Typed Catch is certainly something that falls under the category of Pattern Matching, but the currently proposed syntax of try {} catch () {} catch () {} catch () {}... does not look like a syntax that can be reused by Pattern Matching at all. This means that if in the future we have Pattern Matching, we will have to invent another syntax for Pattern Matching, and be forced to choose between:

Neither sound attractive to me to be honest.

zenparsing commented 8 years ago

I see.

It's going to be extremely difficult to evaluate the tradeoffs without being able to compare to a concrete syntax. The Scala syntax of using "=>" won't work, given arrow functions.

In the absence of something concrete, the challenge is going to be balancing the additional power of pattern matching with the increase in verbosity and the learning curve it requires.

Personally, I'm not bullish on JS ever getting a pattern matching syntax, because as far as I can tell that would necessitate yet another object-literal-like syntax whose semantics are subtly distinct from destructuring. For destructuring, we consciously chose irrefutable patterns, whereas pattern matching wants refutable patterns.

phpnode commented 8 years ago

Sorry for putting this comment here I wasn't sure it really warranted a new issue:

It really seems to me that pattern matching and conditional catch could be unified if JS were to adopt an optional flow/TS like type syntax, for example:

Conditional Catch:

type CustomError = Error & {name: 'CustomError' };

try {
  doSomething();
}
catch (e: TypeError) {
  handleTypeError(e);
}
catch (e: CustomError) {
  handleCustomError(e);
}
catch (e) {
  handleError(e);
}

Pattern Matching:

type User = {
  name: string;
  email: string;
};

type RegularUser = User & { role: 'regular' };
type AdminUser = User & { role: 'admin' };

const user = match(loadUser(), [
  (user: RegularUser) => checkPassword(user) ? user : null, // just an arrow function
  (user: AdminUser) => {
    if (!checkPassword(user)) {
      return null;
    }
    grantSuperPowers(user);
    return user;
  },
  (_) => null // default clause, accepts anything because it has no typed parameters.
]);

The subject of types in ES has been brought up many times and there's always been resistance but with the emergence of flow and typescript perhaps things are changing on that front. We're not likely to get pattern matching in this form in the near future, but it would be possible to implement conditional catch in a way which would be forward compatible with a ts/flow like type system - the type annotation should point to a class that the error is an instance of -

class VeryBadError extends Error {}

try {
  if (Math.random() > 0.5) {
    throw new VeryBadError('A very bad thing happened');
  }
  else {
    throw new Error('A bad thing happened');
  }
}
catch (e: VeryBadError) {
  console.error(e.message);
}
catch (e) {
  console.info(e.message);
}

// Desugars to:

try {
  if (Math.random() > 0.5) {
    throw new VeryBadError('A very bad thing happened');
  }
  else {
    throw new Error('A bad thing happened');
  }
}
catch (e) {
  if (e instanceof VeryBadError) {
    console.error(e.message);
  }
  else {
    console.info(e.message);
  }
}