You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I would love to see support for compiler calculated constants:
Things like:
var msg = 'Hello'+' '+'world'+'!';
var ratio = 3/1;
var height = 30;
var width = height * ratio;
i like them to be translated to something like this:
var msg = 'Hello world!'; // 'Hello'+' '+'world'+'!'
var ratio = 3; // 3/1
var height = 30;
var width = 90; // height * ratio
This will allow writing a more readable code, without paying the computational price at run-time.
This idea can be expanded to also support pre-calculation of functional only code:
module x {
function fib(n) { // yes i know very naive implementation with no thinking about performance
if (n <= 0) return 1;
return fib(n-1)+fib(n-2);
}
export var v = fib(5);
}
can be translated to:
var x;
(function (x) {
// omitted since it is unused outside its scope, and all its uses are pre-calculated
// function fib(n) {
// if (n <= 0)
// return 1;
// return fib(n - 1) + fib(n - 2);
// }
x.v = 8; // fib(5)
})(x || (x = {}));
this will force the compiler to mark as part of its type information. whether something is functional or has side effects (accessing anything outside its internal scope), and where code is usable from, and whether or not it is ever used from within any function that already exists.
Are you aware of any existing languages that behave like this? The two parts of your request are fairly distinct from one another and both large, fundamental pillars of a type system. I'm not sure of any languages that do the first suggestion (maybe some techniques with C++ template metaprogramming) while the latter is a core feature in some functional languages but which have huge ramifications for the language as a whole. It's hard to imagine us doing either of these things given our current goals and constraints.
We might do constant propagation, but I don't think there's any chance of evaluating user functions at compile-time. What if fib has a bug and hangs? Plus, the set of useful functions you could write that don't depend on possibly-mutated values in the global scope is basically empty.
I agree that it is two separate requests, C++ currently support both - and this gives a lot of power to C++.
In my opinion C++ and TypeScript share the compiler phase as their source of power, the fact that the code you write is not the code being run, but instead there is a compiler that enhances it and translate it gives a huge amount of power. This together with the fact that build CPU cycles are a lot cheaper then runtime cpu cycles (both in computation intensive programs, and real time where c++ dominates and in browser where TypeScript resides)
Currently i think TypeScript does not make enough use of that source of power, and i would like to see more of that being used.
The first part, pre-calculation of constants is very easy to perform, but i think it is best done in the context of the second one, and not part of minification.
Analysis of what function/variable is accessible from outside and what not is very easy to do (the compiler already check for accessibility when you try to reference a symbol) If a symbol is not accessible from outside - and not from a code that is references from outside - it should be a natural candidate to be optimized out (call it elimination of simple case of dead code) - i think everyone will appreciate a compiler warning if something like this is found (and here we have another feature request in this thread)
Finding out whether a function has side-effects is just as easy:
If the symbol is accessible from the global scope - it has the potential of a side-effect since it can be proxied by other code
If it uses some other symbol that is outside its own scope (instance variable, module variable, global accessible symbol... - It has a side-effect
Anything else can be treated as functional code - and i really see no reason not to execute it by the compiler (that is written in TypeScript inside some JS VM) and use the resulting value instead of the call itself.
I agree that opening this up will offer a completely new way to program in TypeScript that is not avaliable in JS - TypeScript meta programming. But i see that as a great thing, not as a problem.
if there is a bug in my code - i prefer my code to hang during compilation (timeout can server as a way to handle this) and not at run-time. In fact every error i can move over to the compile time is a benefit in my mind (this is the major advantage of Type safety over contract libraries)
Writing code that does not access global values but accept the necessary values as parameters is considered good coding practice and is encouraged almost everywhere - that does not mean that the values do not come from the global world, it just means that the function does not access the global world aside from the parameters it gains.
(And yes this is a complication i did not address in my previous comment - if the code calls a function of a given parameter, or changes it - then it has a side effect)
But i disagree that there are only a limited number of functions that do that, here are examples:
template generation, function wrapping, math functions, string manipulations are all functions that exists in this manner (yes there are also other variations of them), JS is talked about as enabling functional programming - and functional programming is exactly what i am referring too here.
We do have one problem more complication i did not addressed with executing code at compile time - and that is function objects and instance objects - those will have to be replaced by code that generates them by the compiler, this can lead to a slightly simplified code, but still code.
I'd love to see a compile time option that does optimization techniques. I don't think it should be the default (I love that typescript is like 95% the same as the generated javascript), but I do think it should be available.
We might do constant propagation, but I don't think there's any chance of evaluating user functions at compile-time.
I don't think there's any need to. You just need dead code elimination, constant propagation and an inliner. It wouldn't necessarily handle this specific case with the fib code, but it could optimize away a lot of similar functions. (In theory it could handle this case through repeated inlining, but it probably shouldn't handle recursive functions like this, could cause the compiler to take minutes just optimizing fib(200)).
I agree that it should be controlled by compiler options, maintaning the
code as is is great for debugging.
I disagree about function inlining this is where the JS VMs are already
strong.
Calculating fib(200) will probably fail anyway due to stack overflow, but
if i make use of some large primery number that i generate using some
formula/algorithm it will be nice if this could be optimized on my build
server before deployment. And i dont really care how long that will take.
And once the call is replaced by the result, then dead code elimination can
kick in.
While we talk about compiler options, it will be nice to get the
information the compiler gather on each symbol as a comment next to its
declaration (triggered by option of course, and here is one more feture in
this thread :-) )
בתאריך 23 בספט 2014 15:46, "Nathan Jervis" [email protected] כתב:
I'd love to see a compile time option that does optimization techniques. I
don't think it should be the default (I love that typescript is like 95%
the same as the generated javascript), but I do think it should be
available.
We might do constant propagation, but I don't think there's any chance of
evaluating user functions at compile-time.
I don't think there's any need to. You just need dead code elimination,
constant propagation and an inliner. It wouldn't necessarily handle this
specific case with the fib code, but it could optimize away a lot of
similar functions. (In theory it could handle this case through repeated
inlining, but it probably shouldn't handle recursive functions like this,
could cause the compiler to take minutes just optimizing fib(200)).
—
Reply to this email directly or view it on GitHub #720 (comment)
.
I'm not sure why this would be something you'd need the compiler to do. This sort of constant propagation is most appropriately handled by the actual runtime (which has all the real data to do this accurately).
Yes this is true that the runtime does all of this.
Perhaps if an optimizing compiler is to be considered, a different code gen philosophy could be considered. Perhaps TypeScript could generate asm.js as a compiler target. With that compile time optimizations would likely be appreciated.
Yeah so basically no shot. Better off with compiling to something like LLVM then, which is a LOT more work than the transpiling that typescript currently does.
When i write typescript i write it to run on the JS runtime in the node.js or browser envirenment i intend for it to run.
but i use the compiler as a tool to make my programming life easier.
I use it to ease my code by using a better syntax (TypeScript & ES6 over ES5/3)
I use it to help me catch simple logic errors by specifying type restrictions wherever appropriate (knowing that those restrictions are compile time only)
I use linting (another compiler) to help me generate more readable code
if that compiler phase can take one more step and optimize my code it will be great.
Elimination of dead code is one way of doing it.
Calculating all values that it is possible to calculate at compiler time rather then at runtime is another great way.
Eliminating scope held instances of unreachable code is a third one.
This is not the same as compile to asm.js or LLVM since i still want it to run on the same runtime environment i intended for it to run in the first place - with the same rules about what can be done to it. With the same ability to mold the resolting JS objects using other (including non typescript) code.
I am looking over some code written by others in the last few days, it is filled with utility functions that are only accessible from a certain code segment, and are only used when the code load (static initialization) - most of this code can be optimized out. I do not want to do that by hand, since the separation of that code to its own functions makes sense to me in terms of code design and readability. but why maintain all that code in the runtime? those are functions that are memory leak in their nature. they are defined inside a specific scope, and can not be accessed from outside, they are called once and only once at the initialization of that scope, and then those function objects just sit around with their code until the runtime dismiss their scope (possibly never).
A good VM will know to page out those function objects, so they will sit on the disk rather then on memory - but who said my code run on a good VM? All i do know is that it will run on a VM, and that it passed through my compiler - so whatever work the compiler can offload from the VM, the better.
Will something like this create a new different language - it sure will, currently TypeScript is good for people that want to write JS with types (C with classes comes to mind) but the compiler offer a power that can lift TypeScript to higher places, and still maintain it JS compatible.
I forked TypeScript to see if i can start working on this on my own, not sure how slowly that will take me, but if anyone is willing to help, just send a relevant PR
once some milestone is reached, i will send a PR back to here
Lee Elenbaas (@lee-elenbaas) dead code elimination and constant propagation get you the majority of what you want here. The fib optimization is a very controversial optimization, since recursive function definitions could very easily take minutes, or hours to run, if it finishes at all.
The google closure compiler actually optimizes javascript like this (in fact doing all of the optimizations except the fib one if you have advanced optimizations on). My recommendation would be to pass it to closure as a post-build step. Any additional optimizations (like the fib one) would be useful to the closure compiler as well, so I'd suggest forking that if you'd like to add optimizations.
It's possibly worth investing time to see if there are certain optimizations that closure could make if it had access to the type information, but offhand I'm unaware of any serious optimizations it could make (there are certainly many that the runtime could make, but closure still compiles to javascript)
Activity
danquirk commentedon Sep 22, 2014
Are you aware of any existing languages that behave like this? The two parts of your request are fairly distinct from one another and both large, fundamental pillars of a type system. I'm not sure of any languages that do the first suggestion (maybe some techniques with C++ template metaprogramming) while the latter is a core feature in some functional languages but which have huge ramifications for the language as a whole. It's hard to imagine us doing either of these things given our current goals and constraints.
mhegazy commentedon Sep 22, 2014
constant propagation or constant folding is a common compiler optimization. I would see us doing something like that if we support minification.
RyanCavanaugh commentedon Sep 22, 2014
We might do constant propagation, but I don't think there's any chance of evaluating user functions at compile-time. What if
fibhas a bug and hangs? Plus, the set of useful functions you could write that don't depend on possibly-mutated values in the global scope is basically empty.lee-elenbaas commentedon Sep 23, 2014
I agree that it is two separate requests, C++ currently support both - and this gives a lot of power to C++.
In my opinion C++ and TypeScript share the compiler phase as their source of power, the fact that the code you write is not the code being run, but instead there is a compiler that enhances it and translate it gives a huge amount of power. This together with the fact that build CPU cycles are a lot cheaper then runtime cpu cycles (both in computation intensive programs, and real time where c++ dominates and in browser where TypeScript resides)
Currently i think TypeScript does not make enough use of that source of power, and i would like to see more of that being used.
The first part, pre-calculation of constants is very easy to perform, but i think it is best done in the context of the second one, and not part of minification.
Analysis of what function/variable is accessible from outside and what not is very easy to do (the compiler already check for accessibility when you try to reference a symbol) If a symbol is not accessible from outside - and not from a code that is references from outside - it should be a natural candidate to be optimized out (call it elimination of simple case of dead code) - i think everyone will appreciate a compiler warning if something like this is found (and here we have another feature request in this thread)
Finding out whether a function has side-effects is just as easy:
Anything else can be treated as functional code - and i really see no reason not to execute it by the compiler (that is written in TypeScript inside some JS VM) and use the resulting value instead of the call itself.
I agree that opening this up will offer a completely new way to program in TypeScript that is not avaliable in JS - TypeScript meta programming. But i see that as a great thing, not as a problem.
lee-elenbaas commentedon Sep 23, 2014
Ryan Cavanaugh (@RyanCavanaugh) I disagree on both points:
if there is a bug in my code - i prefer my code to hang during compilation (timeout can server as a way to handle this) and not at run-time. In fact every error i can move over to the compile time is a benefit in my mind (this is the major advantage of Type safety over contract libraries)
Writing code that does not access global values but accept the necessary values as parameters is considered good coding practice and is encouraged almost everywhere - that does not mean that the values do not come from the global world, it just means that the function does not access the global world aside from the parameters it gains.
(And yes this is a complication i did not address in my previous comment - if the code calls a function of a given parameter, or changes it - then it has a side effect)
But i disagree that there are only a limited number of functions that do that, here are examples:
template generation, function wrapping, math functions, string manipulations are all functions that exists in this manner (yes there are also other variations of them), JS is talked about as enabling functional programming - and functional programming is exactly what i am referring too here.
We do have one problem more complication i did not addressed with executing code at compile time - and that is function objects and instance objects - those will have to be replaced by code that generates them by the compiler, this can lead to a slightly simplified code, but still code.
mirhagk commentedon Sep 23, 2014
I'd love to see a compile time option that does optimization techniques. I don't think it should be the default (I love that typescript is like 95% the same as the generated javascript), but I do think it should be available.
Ryan Cavanaugh (@RyanCavanaugh)
I don't think there's any need to. You just need dead code elimination, constant propagation and an inliner. It wouldn't necessarily handle this specific case with the fib code, but it could optimize away a lot of similar functions. (In theory it could handle this case through repeated inlining, but it probably shouldn't handle recursive functions like this, could cause the compiler to take minutes just optimizing fib(200)).
lee-elenbaas commentedon Sep 23, 2014
Nathan (@nathan)
I agree that it should be controlled by compiler options, maintaning the
code as is is great for debugging.
I disagree about function inlining this is where the JS VMs are already
strong.
Calculating fib(200) will probably fail anyway due to stack overflow, but
if i make use of some large primery number that i generate using some
formula/algorithm it will be nice if this could be optimized on my build
server before deployment. And i dont really care how long that will take.
And once the call is replaced by the result, then dead code elimination can
kick in.
While we talk about compiler options, it will be nice to get the
information the compiler gather on each symbol as a comment next to its
declaration (triggered by option of course, and here is one more feture in
this thread :-) )
בתאריך 23 בספט 2014 15:46, "Nathan Jervis" [email protected] כתב:
nathan commentedon Sep 23, 2014
You probably meant Nathan Jervis (@mirhagk).
lee-elenbaas commentedon Sep 23, 2014
Probably, from my cellphone email i have limited view of the gh names
בתאריך 23 בספט 2014 23:06, "Nathan Dinsmore" [email protected] כתב:
CyrusNajmabadi commentedon Sep 24, 2014
I'm not sure why this would be something you'd need the compiler to do. This sort of constant propagation is most appropriately handled by the actual runtime (which has all the real data to do this accurately).
mirhagk commentedon Sep 24, 2014
Yes this is true that the runtime does all of this.
Perhaps if an optimizing compiler is to be considered, a different code gen philosophy could be considered. Perhaps TypeScript could generate asm.js as a compiler target. With that compile time optimizations would likely be appreciated.
RyanCavanaugh commentedon Sep 24, 2014
See #375 re: asm.js.
mirhagk commentedon Sep 24, 2014
Yeah so basically no shot. Better off with compiling to something like LLVM then, which is a LOT more work than the transpiling that typescript currently does.
lee-elenbaas commentedon Sep 29, 2014
When i write typescript i write it to run on the JS runtime in the node.js or browser envirenment i intend for it to run.
but i use the compiler as a tool to make my programming life easier.
I use it to ease my code by using a better syntax (TypeScript & ES6 over ES5/3)
I use it to help me catch simple logic errors by specifying type restrictions wherever appropriate (knowing that those restrictions are compile time only)
I use linting (another compiler) to help me generate more readable code
if that compiler phase can take one more step and optimize my code it will be great.
Elimination of dead code is one way of doing it.
Calculating all values that it is possible to calculate at compiler time rather then at runtime is another great way.
Eliminating scope held instances of unreachable code is a third one.
This is not the same as compile to asm.js or LLVM since i still want it to run on the same runtime environment i intended for it to run in the first place - with the same rules about what can be done to it. With the same ability to mold the resolting JS objects using other (including non typescript) code.
I am looking over some code written by others in the last few days, it is filled with utility functions that are only accessible from a certain code segment, and are only used when the code load (static initialization) - most of this code can be optimized out. I do not want to do that by hand, since the separation of that code to its own functions makes sense to me in terms of code design and readability. but why maintain all that code in the runtime? those are functions that are memory leak in their nature. they are defined inside a specific scope, and can not be accessed from outside, they are called once and only once at the initialization of that scope, and then those function objects just sit around with their code until the runtime dismiss their scope (possibly never).
A good VM will know to page out those function objects, so they will sit on the disk rather then on memory - but who said my code run on a good VM? All i do know is that it will run on a VM, and that it passed through my compiler - so whatever work the compiler can offload from the VM, the better.
Will something like this create a new different language - it sure will, currently TypeScript is good for people that want to write JS with types (C with classes comes to mind) but the compiler offer a power that can lift TypeScript to higher places, and still maintain it JS compatible.
lee-elenbaas commentedon Sep 29, 2014
I forked TypeScript to see if i can start working on this on my own, not sure how slowly that will take me, but if anyone is willing to help, just send a relevant PR
once some milestone is reached, i will send a PR back to here
mirhagk commentedon Sep 29, 2014
Lee Elenbaas (@lee-elenbaas) dead code elimination and constant propagation get you the majority of what you want here. The
fiboptimization is a very controversial optimization, since recursive function definitions could very easily take minutes, or hours to run, if it finishes at all.The google closure compiler actually optimizes javascript like this (in fact doing all of the optimizations except the
fibone if you have advanced optimizations on). My recommendation would be to pass it to closure as a post-build step. Any additional optimizations (like thefibone) would be useful to the closure compiler as well, so I'd suggest forking that if you'd like to add optimizations.It's possibly worth investing time to see if there are certain optimizations that closure could make if it had access to the type information, but offhand I'm unaware of any serious optimizations it could make (there are certainly many that the runtime could make, but closure still compiles to javascript)
lee-elenbaas commentedon Oct 6, 2014
Nathan Jervis (@mirhagk) After diving in a bit to closure, i think your are right
thanks for the pointer