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, 8/25/2021 #45577

Closed
DanielRosenwasser opened this issue Aug 25, 2021 · 3 comments
Closed

Design Meeting Notes, 8/25/2021 #45577

DanielRosenwasser opened this issue Aug 25, 2021 · 3 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

The Awaited Type in lib.d.ts

#45350

  • When we introduced await into TypeScript, we added some logic to recursively unwrap a type.

  • At the time, we had no recursion mechanism within the type system.

  • First we started out with an awaited type operator.

  • Problem: if you have generics, you end up with higher-order type operators.

    async function f<T>(x: T) {
        await x; // awaited T
    }
    • This is technically correct, but very cumbersome.
    • The problem is that awaited T is not necessarily assignable to T (nor vice versa).
  • Had ideas for a top-level type alias...

  • And that's what we do here, but right now are only introducing the alias to be used in lib.d.ts.

    • Behavior is based on what the await operator does.
  • Preserves the incorrect behavior to avoid errors on awaited T -> T in async functions, but better-models lib.d.ts.

  • Ideally, other overloads take care of the breaks.

  • Does this break existing code?

    • Probably, but question is how much.
    • Have to run our tests.
  • Having Awaited makes sense in general.

  • Worth thinking about whether users will be able to understand how to use this.

  • Should we get rid of the other overloads?

    • Want to try to!
  • Conclusions

    • Need to see what this breaks.
    • Need to see if we can remove overloads.
    • Maybe see if this can be leveraged by await?

--noErasingImportedNames

#44619

  • Current behavior is that if you write an import for something, if you don't use it you lose it.
    • [[How did nobody think of phrasing it that way until now?]]
  • Under this mode, every imported name is preserved if it has a value meaning.
  • Under isolatedModules, it will warn you that some compiler won't know how to drop types.
    • Then the name is a misnomer. It's about dropping values.
    • Great, we can bikeshed this.
  • Who would use this?
  • Useful for
    • Svelte.

      <script>
      import Layout from "./Layout.svelte";
      </script>
      <Layout />
      • TypeScript doesn't see what the transformer does, needs to not drop Layout.
    • Vue

      • Not vanilla Vue SFCs. <script setup>.
      • Similar to Svelte use-case - top-level declarations are implicitly brought into the template code.
  • noErasingImportedNames hard to parse.
    • preserveValueImports
      • "importsNotUsedAsValues": "preserve" 😢

Stack Overflow on Type Instantiation

#45576

  • We increased the type instantiation limit from 50 to 500.
  • How do we know we're about to blow the stack?
    • Nothing we can do?
  • Least common denominator?
    • Changing target, could be dynamic on the browser itself.
  • Can try to guard against these with a try/catch.
    • try/catch at the top of instantiateType?
    • Could, but how do you guard against inconsistent state?
  • Could pre-emptively blow the stack and create a guesstimate from that.
    • [[Think my idea just got laughed down. 😅]]
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Aug 25, 2021
@awerlogus
Copy link

awerlogus commented Aug 26, 2021

@DanielRosenwasser have you tried to solve the stack overflow problem using trampoline technique?

const trampoline = f => (...args) => {
  let result = f
  
  do { result = result(...args) }
  while (typeof result === 'function')
    
  return result
}

const factorial = n => factorialAcc(1n, n)
  
const factorialAcc = (acc, n) => {
  if (n <= 1) { return acc }
  
  return () => factorialAcc(acc * BigInt(n), n - 1)
}

const factorialT = trampoline(factorial)

console.log(factorialT(300))

Performance tests for the large call stack case:

No trampoline test for factorial(2500):  8.612300038337708 ms
No trampoline test for factorial(2500):  8.430999994277954 ms
No trampoline test for factorial(2500):  8.416900038719177 ms
No trampoline test for factorial(2500):  8.343400001525879 ms
No trampoline test for factorial(2500):  8.81769996881485 ms

Trampoline test for factorial(2500):  6.159500002861023 ms
Trampoline test for factorial(2500):  6.034699976444244 ms
Trampoline test for factorial(2500):  6.161700010299683 ms
Trampoline test for factorial(2500):  5.963100016117096 ms
Trampoline test for factorial(2500):  5.686100006103516 ms

Performance tests for the small call stack case:

No trampoline test for factorial(100):  0.05640000104904175 ms
No trampoline test for factorial(100):  0.0591999888420105 ms
No trampoline test for factorial(100):  0.07770001888275146 ms
No trampoline test for factorial(100):  0.06279999017715454 ms
No trampoline test for factorial(100):  0.05779999494552612 ms

Trampoline test for factorial(100):  0.08009999990463257 ms
Trampoline test for factorial(100):  0.08600002527236938 ms
Trampoline test for factorial(100):  0.0835999846458435 ms
Trampoline test for factorial(100):  0.0821000337600708 ms
Trampoline test for factorial(100):  0.08160001039505005 ms

@DanielRosenwasser
Copy link
Member Author

DanielRosenwasser commented Aug 26, 2021

I didn't capture it in the notes but

  1. We raised generators as an option, and the overhead of generators (both down-leveled for ES5, and maintained for ES2015 targets) had a considerable performance hit.
  2. Re-writing with a manual trampoline (as @weswigham did for other constructs like checking binary operands) is possible, but makes the code harder to maintain and update in our opinion. I don't know if our trampolining works similarly to what you have up there.

Wesley can correct me if I got any of the details wrong.

@weswigham
Copy link
Member

I mean, our existing trampolines function as state machines rather than recreating closures all the time, but yes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

3 participants