Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design Meeting Notes, 5/27/2016 #8908

Closed
DanielRosenwasser opened this issue May 31, 2016 · 5 comments
Closed

Design Meeting Notes, 5/27/2016 #8908

DanielRosenwasser opened this issue May 31, 2016 · 5 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

Narrowing within Function Expressions

const Modifiers on Function Parameters

functin f0() {
    let x = getStringOrNumber();
    if (typeof x === "string") {
        const f = () => x.length; // currently errors
    }
}
  • We don't make an effort to dive within functions expressions - we don't narrow within them.
    • However, when you have a const, you know for sure that you can definitely narrow.
    • We're making sure that works.
    • Problem is, you can't reflect that behavior with parameters today.
  • In a constructor can you have a public const?
    • We could say public readonly does that?
      • That's conflating the idea.
      • "It's uninteresting" and doesn't come up in practice
  • What about nested members of a const?
    • i.e.

      const x = { foo: { bar: number } };
      x.foo.bar;

IIFEs

Repro from #8381

(function() {

}())
  • Look at the PR

Enums & Algebraic Data Types

  • Simple approach is that you use singleton value types.

    • Currently, these singleton value types are limited to string literal types.

      type Foo = "up" | "down";
      
      var x: Foo;
      switch (x) {
          case "up":
            // ...
            break;
          case "down":
            // ...
            break;
      }
    • Gets more interesting with something like:

      interface Up {
          kind: "uP";
          speed: number;
      }
      
      interface Down {
          kind: "down";
          fatal: boolean;
      }
      
      var x: Up | Down;
      switch (x.kind) {
          case "up":
            x.speed;
            break;
          case "down":
            x.fatal;
            break;
      }
      • Forgive the terrible example.
    • These need not be string literals - they could be numbers, enums.

    • Wouldn't it be nice if we could do this sort of thing with SyntaxKind in the compiler?

      • Each node would define its own specific discriminator in its kind field.

        interface Node {
            kind: SyntaxKind;
        }
        
        interface PropertyAccess {
            kind: SyntaxKind.PropertyAccess;
        }
      • Problematically, you couldn't quite do this with the OO-style property inheritance we get (open-ended in OOP, close-ended in FP).

      • Ideally, you could do exhaustiveness checking with this, but Node is open-ended right now.

        • So you'd do something like this:

          interface NodeBase {
              kind: SyntaxKind;
          }
          
          interface PropertyAccess {
              kind: SyntaxKind.PropertyAccess;
          }
          
          interface CallExpression {
              kind: SyntaxKind.CallExpression;
          }
          
          type Node = PropertyAccess | CallExpression // | ...
        • What would the type of a Node's kind be now?

          • SyntaxKind.ProperyAccess | SyntaxKind.CallExpression | ....

          • Is SyntaxKind equivalent to that union type?

          • Not necessarily! That can be confusing!

          • So you'd have to do something like this:

            const enum Color {
                Red, Green, Blue
            }
            
            type AllColors = Color.Red | Color.Green | Color.Blue;
  • So to avoid the AllColors example, you could imagine that we'd have a new kind of enum:

    union enum Color {
        Red,    // 0
        Green,  // 1
        Blue    // 2
    }
    • Couldn't use flags in this example.
  • TC39 convo on enums had symbols in mind.

    • Issues with that.
      • Need to be able to serialize that in an error message.
  • DR: I think we could do ADT-style stuff without needing enum types yet.

    • AH: Yes, and strings are fine, but you really can't do that with numbers.
    • DR: But ADTS are moe general.
    • AH: But string/number discriminators are the real value of this feature.
    • DR: Agreed.
  • DR: Problem is that the base-type/intermediate-types/union-type pattern is cumbersome - you've fixed the enum type, done nothing there.

@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label May 31, 2016
@yortus
Copy link
Contributor

yortus commented Jun 2, 2016

There are a lot of tools that use the Mozilla Parser API to represent ASTs (babel, esprima, escodegen, acorn, and their respective plugins, etc).

That defines a Node like:

interface Node {
    type: string;
}

Then all the node types extend that, e.g.:

interface IfStatement extends Node {
    type: "IfStatement";
    test: Expression;
    consequent: Statement;
    alternate: Statement;
}

interface LogicalExpression extends Node {
    type: "LogicalExpression";
    operator: "||" | "&&";
    left: Expression;
    right: Expression;
}

You can see a full set of parser API typings here.

Similar to the meeting notes example, it would be great when dealing with these AST to be able to write:

function foo(node: Node) {
    if (node.type === 'Identifier') {
        node.name;
    }
    else if (node.type === 'MemberExpression') {
        node.object;
        node.property;
    }
    else {
        ...
    }
}

@DanielRosenwasser
Copy link
Member Author

Yup @yortus that's the general direction we're hoping to land in.

@RyanCavanaugh
Copy link
Member

Was there an outcome on const parameters?

@weswigham
Copy link
Member

I'd also like to reference #6062 in regard to the ADT work. Nothing has to be/should be specific to singleton types or enums - switching based on any kind of discriminating type structure should probably be possible. I could probably just clean that PR up and get feedback and we'd have what we want there.

I also have a half done implementation of exactly this:

interface PropertyAccess {
    kind: SyntaxKind.PropertyAccess;
}

based around my numeric literal types PR (though enum member types are mostly separate from them). Though most of my 'open questions' to myself from those resulted in questioning how they should be compatible with each other and numeric literals. I could clean that up a bit and divorce it from the numeric literal work if we're interested in it.

@DanielRosenwasser
Copy link
Member Author

@RyanCavanaugh not much actual discussion on it as much as the details on it to be honest.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

4 participants