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

Question: benefits of nested function syntax #3009

Closed
tinganho opened this issue May 3, 2015 · 8 comments
Closed

Question: benefits of nested function syntax #3009

tinganho opened this issue May 3, 2015 · 8 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@tinganho
Copy link
Contributor

tinganho commented May 3, 2015

Out of curiosity, I'm just wondering what kind of benefits of having the nested function syntax(currently in the TS repo) as oppose to having a class-based?

You are the only OSS I've seen programming in this fashion.

@DanielRosenwasser
Copy link
Member

Don't do it man. You don't want to go down this rabbit hole.

But since you already asked, I'll offhandedly answer some of it. This is yet another holy war in the world of programming. You will find people on extremes of the functional vs OO debate. There are obviously merits to both and you should attempt to pick the right tool for the job.

The original reason that the compiler was rewritten the way it was, was for simplicity and speed over the old compiler. I think it afforded us both, though I don't personally know enough to say how much of a hand function closures vs objects played in that speed boost.

Here are some of my personal opinions on how things stand right now, and don't necessarily reflect the view of the rest of the team:

  • When you're simply trying to perform a monolithic operation, it doesn't make sense to encapsulate that operation with a class. What you're looking for is a set of functions, ideally that can be grouped into a module. Classes are often overkill for this sort of thing
    • If you cut away the logic that supports language service scenarios, the command-line parser, checker, and emitter exhibit this behavior. In fact, this is the reason why functional languages are typically touted as great for compilers.
  • If you're traversing a tree, functions are just great.
    • The visitor pattern is a nice abstraction, but it's brittle, and alternative solutions I've seen are usually hacky. Since almost everything in the compiler is a tree traversal, things work out nicely the way they are.
  • When you're trying to incrementally ask for new results over time, and you need to be able to change behavior with new inputs over time, classes/objects might just be the way to go.
    • I think the scanner is an ideal candidate for being written as a class. Encapsulating the behavior in a closure is admittedly a little awkward.
    • The irony of this is that the language service needs this type of behavior from the parser and typechecker, so they've been adapted to accommodate that need. They're still not written as classes, but I'm sure classes would still come with different tradeoffs.

There are other use cases, but this is mostly just limited to things that stick out.

You are the only OSS I've seen programming in this fashion.

I'll be frank with you - the only other projects I know of that do this are other compilers for functional languages 😄 (e.g. F#, MLton, etc.)

I'd say that a big reason most people use classes is that languages like Java, C#, and VB.NET require you to write all your code within classes, which is not necessarily a bad thing (frankly, I think there's a lot of good in it), but it has certainly had a long-lasting effect on the way in which people approach problems and write code.

There's also been a lot of software engineering formalization of OO in both research and industry (e.g. GoF Design Patterns, SOLID, etc.). These ideas are somewhat well-understood in OO, whereas things still seem new on the FP terrain for most people[1] even if many of the same fundamental principles apply.

[1] Java got lambdas like last year, right?

@DanielRosenwasser DanielRosenwasser added the Question An issue which isn't directly actionable in code label May 3, 2015
@NoelAbrahams
Copy link

Some interesting points...

I'd say that a big reason most people use classes is that languages like Java, C#, and VB.NET require you to write all your code within classes, which is not necessarily a bad thing (frankly, I think there's a lot of good in it), but it has certainly had a long-lasting effect on the way in which people approach problems and write code

That seems to suggest that people writing software are not open to adopting new ways of doing things. I would say, on the contrary, those writing software are _continually_ on the lookout for new ideas. Just look at ReactJS for instance, which departs quite radically from the standard way of writing user interfaces, and yet has significant adoption from the community.

The reason I use classes is that, for the problem I'm trying to solve, there is no better way.

@CyrusNajmabadi
Copy link
Contributor

The primary reason that the new compiler was written this way was to help ensure that we could vet using TypeScript to provide a 'typed javascript' experience. Pre-es6 JavaScript doesn't have classes, so it's not something that community was using. If we were to write our own compiler with classes, and other features not commonly used by the community, then we wouldn't know first hand how good our language was for the kinds of constructs people were actually using.

Because of this, we discovered things wrong with both our language and our IDE experience. For example, some part of generic type inference didn't work well at all in this heavy function style. Similarly, we found that many of our IDE features did not work well when you wrote in this fashion. By attempting to use pure javascript /w types in the compiler, we helped discover these things so we could make TypeScript a better language for JavaScript developers.

Now, that said, had we only used functions and never classes, we'd likely have succumbed to the same problem. Namely, we wouldn't be using things like Classes, and we wouldn't have first-hand experience there of how well those constructs worked. As such, we've written our entire stack as a hybrid of different models. You'll notice that in the testing and services layer that there is extensive use of classes.

Ideally, and what we strive for, is a healthy mix of all language features across our entire compiler (as long as they don't come at too high a cost on ES3/ES5 runtimes). That's why you'll see usage of new ES6 features like:

  1. let/const
  2. for-of
  3. template-literals
  4. destructuring

Indeed, some of us (myself included), are eagerly awaiting (pun-intended) our async work to be completed. Being able to use async/await in the services layer, would make a lot of stuff much nicer (and more scalable to boot).

As for perf, our own measurements have shown classes to be faster depending on what you do. That seems to be because, with a class, the cost of instantiating the functions happens only once (when the class IIFE is executed, and the functions are attached to the prototype). However, with the nested-function-style approach, each time the outer function is called, the inner functions end up being instantiated. So, if you have an outer function you call only a few times, then the benefit of the class is minimal. However, if you call the outer function many times (i.e. thousands or more), then classes tend to pull ahead of the function-style approach.

@CyrusNajmabadi
Copy link
Contributor

An important thing to point out is that we are, in no way, stating that the style we've taken with our project is the style that people should use if they use TypeScript. TypeScript's goal is to be able to provide benefits to your large-scale JavaScript project, regardless of what sort of style you use. About the only time we can't do this is when your project embraces and depends on a heavily dynamic type system. In that case, we may not be a good fit for you.

So if you're a JS person that is happy withES3/ES5 and likes lots of functions and closures as your style, then TS will work great for you. Or, if you're coming from a Class-based world, or you just like the ES6 class feature a lot, then TS will also work well for you. Or, if you like mixing and matching, then TS is still a good choice :)

We want to really dogfood all these scenarios, and that's why our stack embraces all these approaches. It's what lets us know first hand how well our language works, and is a great way to help decide, up front, what areas we should be investing in.

@NoelAbrahams
Copy link

@CyrusNajmabadi, that's a lot of useful information.

We want to really dogfood all these scenarios, and that's why our stack embraces all these approaches

One scenario that I feel is a bit neglected is the one based on Visual Studio projects. We have a set up involving 40+ VS projects - all written in TypeScript. Each project is independent and generates a project_x.d.ts, which is then referenced by other projects. This is similar to how one would work with a C# code-base.

In my opinion, this way of organising your code is quite scalable.

Just to note some of the problems:

Perhaps some part of the compiler test harness could be set up this way so that this scenario is also taken into account?

@tinganho
Copy link
Contributor Author

tinganho commented May 4, 2015

Thanks for all the great answers.

@mhegazy
Copy link
Contributor

mhegazy commented May 4, 2015

@NoelAbrahams our sister team F12 tools in IE, has about the same issue, with multiple projects and having to manage their build in a custom way, and they are running into the same problem; I definitely get your pain and theirs.

Proper P2P is something we are looking into; it is just that we have been busy with ES6 for the past 6 month or so.

@CyrusNajmabadi
Copy link
Contributor

@NoelAbrahams We are experiencing this issue firsthand ourselves. We use .tsconfig projects for our individual project layers (compiler, services, server, harness, etc.), and we've run into all those issues you've mentioned. We're definitely aware of them, and we want to fix them.

@mhegazy mhegazy closed this as completed Jun 12, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants