• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

What Does Everyone Want In A Compiler?

Status
Not open for further replies.
Level 6
Joined
Jul 30, 2013
Messages
282
Even when optimizing that stuff away, it's still going to throw syntax errors on garbage code : )

well u could just .. find the "if <exp> then" and "endif" and treat the body of the block as an opaque string until you have determined the static-ness and then parse only those branches that you cannot eliminate..

i say "just" but ofc it would be a bit haxy in your nice recursive descent parser..
also i don't see too much use in having static ifs whose content is syntactically unsound anyway.. i mean perhaps you *could* do *some* things more concicely.. but most likely that will have a bad effect on your code readability so you shouldnt be doing it anyway..

also.. there has been a lot of hot air over.. "you cant have a dangling if or endif inside there"
I think this says less about the language and more about your horrible code style. You can always achieve the same thing by just having a bit larger block inside the static if in stead of breaking it in to subatomic particles.
it will either work well..
or you have too much spaghetti code and you should be refactoring anyway

(wishlish += very good inlining so factoring out stuff like a civilised person wouldnt kill ur performance)

TLDR;
  • You shouldnt put nonsense inside blocks even if you are technically allowed to
  • I dont consider dissallowing total nonsense inside a static if a big issue
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
well u could just .. find the "if <exp> then" and "endif" and treat the body of the block as an opaque string until you have determined the static-ness and then parse only those branches that you cannot eliminate..

The whole point of this discussion is that it is desired behavior that also branches of standard ifs are parsed and analysed, even if the branch is never taken at all.

What you mean can of course be done with static ifs (like it is already done in vJass - garbage code in dead static if branches isn't parsed at all). But the point was that this makes it necessary to distinguish if and static if and not just if and let the compiler chose which one to chose.

  • You shouldnt put nonsense inside blocks even if you are technically allowed to
  • I dont consider dissallowing total nonsense inside a static if a big issue

The discussion was not about static if, but if being interpreted as either if or static if dependend on the code context ;)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
The problem is the code within the if-block. The static if stuff is fine. The reason why the first is a problem is that the case is ambiguous. There is no way to tell when the user intends the code to be compile time instead of runtime. This is because both cases look the exact same. Same code, same context, etc. Telling them apart by having compile time code be outside of functions won't work because runtime code can be outside of functions too.
 
Level 6
Joined
Jul 30, 2013
Messages
282
can be told apart by the expression.
eg
JASS:
globals
  constant boolean DEBUG
endglobals

function f..
   ...
   if DEBUG then
      // show debug messages
   endif
endfunction
.. should be trivial to tell apart. the expression is a compile time constant that will never ever change at runtime. thus u can treat it as a static if..
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
.. should be trivial to tell apart. the expression is a compile time constant that will never ever change at runtime. thus u can treat it as a static if..

Just because you could doesn't mean its a good idea. There are reasons why basically no language handles ifs like that (but use some extra macro-if with a different syntax, if any).

Of course the whole if will be eliminated by the optimizer if DEBUG is defined as false in your example, making the code behave as if the block was never there. But thats still not equivalent to a static if, because the content of the if will be parsed and analysed before the optimization is done.

Also I fail to see your point in this whole discussion. Whats so bad about writing static if when I mean static if? Making if implicitly static based on the constness of its arguments would make the language more complicated (extra rules), harder to read (if functionality context dependent) and more error prone (more blocks that are not parsed) for basically zero gain (writing if instead of static if).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Er.. I am discussing the code in the block, not the expression. I have no problem optimizing away expressions. I do have a problem with whether the code inside of the block is sometimes not runtime code though. You are defending your point with something that is completely irrelevant. You keep discussing the expression. You are disregarding your first statement regarding the contents of the block. As it stands, I have no intention of making the contents of a runtime block be preprocessor script.
 
Level 6
Joined
Jul 30, 2013
Messages
282
@looking_for_help
But thats still not equivalent to a static if, because the content of the if will be parsed and analysed before the optimization is done.

Are you SURE that matters..?
JASS:
if True then
  BJebugMessage("True")
else
  BJebugMessage("False")
endif
// VS

static if True then
  BJebugMessage("True")
else
  BJebugMessage("False")
endif
I dare you explain how those 2 do NOT do the exact same thing? the only difference is an if-true check if the optimiser sucks and cant get rid of it.(comon cant we even do that?)

so.. when would it matter..? hmm well maybe if you emitted entire functions or globals declarations..
but do you need extend language syntax with new keywords for that really? i argue that its not really necessary, if it matters then ull get a compile error if it cant be decided..

or.. u can use:
JASS:
function ConstantExpression takes boolean returns boolean
    // any call to this function has a constant return value if called from the same location..
    // can be used to case otherwise runtime-variable expressions in to a compile-time snapshot value
endfunction

// RandomReal() >0.5 now behaves like a compile-time constant and you cant force the "optimisation", behaves exactly equivalent to true static if, but 0 extra syntax, just a function call.
if ConstantExpression(RandomReal() >0.5) then
    function f..
    endfunction
else

endif


@Nestharus
no-no-no the code inside the if IS runtime code.
its just deciding which of the inner blocks gets emitted thats the compile time part, that and only that.

i wouldn't dream of making a conditional do something silly like deciding if its contents are run at compile or runtime.

i just noted that given 2 or more options (no else is same as empty case) you can optionally (not sufe if good idea) defer validating the correctness of the blocks contents untill you are sure you cannot optimise it away.

now the degree of validation is another matter (allow nonsense in disgarded blocks? or allow syntatically correct but non-functional things like "call BJdebugMessageWithEpicFakeType("abc") )

frankly the bikeshed looks quite fine enuff.. u can have ur static keyword if you want.

I just wanted to impose on you to focus on flexibility rather that explicit features.
if you can provide a mechanism to achieve something without making it a language feature.. and theres not too much overhead then maybe the language feature can wait..
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
Its really really bad idea to have one keyword for two different things which are not really saying what they mean, and you rely on compiler magic.

What if I have a if branch that relies on compile-time constant, but it is meant to be run-time only if block? Well you cant.

Also your python example is nice, but the fact is, extended Jass, as all Jass morphs has to obey the rules set by Blizzard. Therefore there can not be garbage code anywhere in the whole script, or else the map will not be loadable
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Are you SURE that matters..?
...
I dare you explain how those 2 do NOT do the exact same thing? the only difference is an if-true check if the optimiser sucks and cant get rid of it.(comon cant we even do that?)

I already gave explanations why it does matter...

Just because two pieces of code do the same thing doesn't mean they are equally good/readable/maintainable/whatever. The static if hides syntax errors in the dead branch for example, which is very bad. Also static if can be used in contexts where a runtime if isn't allowed. Thats counterintuitive for a user, because both look the same.

Plus the standard if in Jass behaves like that. That alone is reason enough.

"If the optimizer sucks" is not an argument. If the preprocessor sucks, you have the same problem.

JASS:
// RandomReal() >0.5 now behaves like a compile-time constant and you cant force the "optimisation", behaves exactly equivalent to true static if, but 0 extra syntax, just a function call.
if ConstantExpression(RandomReal() >0.5) then
    function f..
    endfunction
else

endif

Thats ridiculus. Why should a non-constant function like RandomReal magically behave like a compiletime function just because it is used in the context of a compiletime function???
 
Level 6
Joined
Jul 30, 2013
Messages
282
Its really really bad idea to have one keyword for two different things which are not really saying what they mean, and you rely on compiler magic.

they are semantically the same to human beings.. if condition then do this else do that..
programming languages are for humans to understand (i know this is hard to understand for some people but i hope u can grasp this without too much effort..) if it is clear for humans then its ok if it gets compiled down to separate things.
example: switch statements on different values..
JASS:
// if i is some small bounded integer..then
switch(i){
case 1: ...;break
case 2: ...;break
case 3: ...;break
}
//might be compiled down to sth like
goto i*max_case_size;  // computed goto, avoids the epic slowdown that a branch-mispredict would cause but wastes a bit of memory.

//or if the i is larger.. or its a string..
//it might turn in to a hashtable lookup. // slower that previous, but avoids the mispredict hazard.

//or if your language allows arbitrary expressions as case values..
//it might turn in to an if-else chain.. // simplest to undestand, most flexible, non-deterministics slowdows from bracnh-mispredicts on the cpu -> stalls


//but that is OK, it means the same to humans and it does EXACTLY what you think it does so it is OK.

also.. u dont like my proposed if that relies on compiler inferring expression const-ness..
but u like
JASS:
struct S
   static member
endstruct
static if
endif
it really is nice how languages NEVER :p reuse the same word for 2 different things :p


What if I have a if branch that relies on compile-time constant, but it is meant to be run-time only if block? Well you cant.

I fail to see the issue here. if the expression is constant why would you want to spend runtime effort on it? you would only do it if the value cant change... but then its not constant! so it is impossible to get optimised away.

Also your python example is nice, but the fact is, extended Jass, as all Jass morphs has to obey the rules set by Blizzard. Therefore there can not be garbage code anywhere in the whole script, or else the map will not be loadable.

oh really? vjass? cjass? wurst? zinc?? 100% of those examples are SYNTACTIC GARBAGE for world editor. they make 0 sense to world editor, we rely on a whole slew of precompilers to turn this in to JASS2 and this whole thread is essentially a proposal for another one. we cant do what ever the heck we want with the syntax as long as we can somehow express the intent in JASS2 at the end.

I already gave explanations why it does matter...
Thats ridiculus. Why should a non-constant function like RandomReal magically behave like a compiletime function just because it is used in the context of a compiletime function???
it is (ConstantExpression i mean )just a compiler hint, not a real function. just like static, no change at all in semantics. but using the function syntax means you get to avoid adding new syntax.

fine i'll make it easier..
JASS:
// in stead of 
static if expr then
//this
if static(expr) then
// feels nice and cozy now? got ur static..
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
it really is nice how languages NEVER :p reuse the same word for 2 different things :p

Thats a completly different thing, you keep twisting my words... Your suggestion was to omit an extra keyword, not reuse one.


I fail to see the issue here. if the expression is constant why would you want to spend runtime effort on it? you would only do it if the value cant change... but then its not constant! so it is impossible to get optimised away.

The issue is that the programmer doesn't have full control over the behaviour and it can be hard to investigate from a single if that uses many constant expressions and some runtime expressions if the final result is constant or not.

Also, to be consequent you would have to omit the constant modifier at all, because the compiler could infer that on other things as well (member variable declarations and so on). This is getting worse.

it is (ConstantExpression i mean )just a compiler hint, not a real function. just like static, no change at all in semantics. but using the function syntax means you get to avoid adding new syntax.

fine i'll make it easier..

How is that easier than a simple static if? How would you evaluate runtime only functions that have side effects (like CreateUnit) in such a forced compiletime context? You would have to make exceptions. Sorry, but this discussion is getting absurd.
 
Level 6
Joined
Jul 30, 2013
Messages
282
Thats a completly different thing, you keep twisting my words... Your suggestion was to omit an extra keyword, not reuse one.


The issue is that the programmer doesn't have full control over the behaviour and it can be hard to investigate from a single if that uses many constant expressions and some runtime expressions if the final result is constant or not.

Also, to be consequent you would have to omit the constant modifier at all, because the compiler could infer that on other things as well (member variable declarations and so on). This is getting worse.

you very well COULD omit the constant keyword, its not really necessary for the compiler, the compiler can figure it out..
the const there is to prevent the user from accidentally modifying something that he didn't mean to modify.

The static in static if however serves no such purpose. if u do nonsense u will get a compile error. the static here is meant not as a means for the programmer to warn him if he does something unintended, rather is is meant to make the compilers job easier so it doesn't have to infer as much.

making the compilers job easier is an acceptable reason to choose to use static ofc. but i think that doing so will just add unneccessary fluff to the syntax.

How is that easier than a simple static if? How would you evaluate runtime only functions that have side effects (like CreateUnit) in such a forced compiletime context? You would have to make exceptions. Sorry, but this discussion is getting absurd.

you wouldn't.. you would determine the expression be unreducible at compile time and defer it to runtime. then either the code will work fine (90% of use cases) or it will throw a compile error because the code inside can't be compiled to something that you can put in a runtime if (function definitions for example). this is all compile time, there is NO exceptions at all.

(notice tho there are a ton of real life exceptions that happen in code: 1./0. anyone? those throw exceptions.. for war3.. and they are just disgarded and the thread crashes.)

anyway all you need do is sth like
JASS:
def stati_if(cnd,block1,block2):
    if eval(cnd):
        return eval(block1)
    else:
        return eval(block2)
and you dont even need a special construct anyway so why are you so obsessed with having a specialized variant of an if statement just so the compiler has a slightly easier time?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
JASS:
function myMagicalFunction takes nothing returns integer
    if GetUnitAbilityLevel(null, nonConstGlobal) then
        set global = global + 1
        return global
    endif
    return 5
endfunction

function a ...
    if myMagicalFunction() == 5 then
        garbage BLARG!!!
    endif
endfunction

While we all can see GetUnitAbility(null, ...) returns false for any null, it would be rather hard for the compiler to actually tell. Also it is almost impossible for you to tell whether it will return constant value or not just by looking at the function a without seeing definition of myMagicalFunction, because you would have to essentially hardcode most of common.j into this behaviour


1/0. doesnt throw anything in C or C++
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
you wouldn't.. you would determine the expression be unreducible at compile time and defer it to runtime. then either the code will work fine (90% of use cases) or it will throw a compile error because the code inside can't be compiled to something that you can put in a runtime if (function definitions for example).

So getting a compile error in 10% of the cases just because an optimization step (!!) fails is good and desired behavior?

You suggested that CompiletimeFunction(RuntimeFunction()) is a compile time value. Thats a) not possible for all functions (you would have to treat some runtime functions different than others) and b) limits the programmer - what if I want a new random real in your example every time the function is exectued (which is usually the case)?
 
Level 6
Joined
Jul 30, 2013
Messages
282
JASS:
function myMagicalFunction takes nothing returns integer
    if GetUnitAbilityLevel(null, nonConstGlobal) then
        set global = global + 1
        return global
    endif
    return 5
endfunction

function a ...
    if myMagicalFunction() == 5 then
        garbage BLARG!!!
    endif
endfunction
that would just try to compile as a regular if and then fail with compile error.

if u used "static if" (GetUnitAbilityLevel) would throw a compile error anyway so this example is MOOT.

.. or u can claim "no it wouldnt, it would work just fine" but that would only be true if the compiler evaluated the function.. so then you have to teach the compiler how the function works... and if you do that then it WOULD compile just fine cuz the compiler would be smart enuff to reduce the expression to a compile time constant. losing any and all reason to have static keyword anyway.

"it would be rather hard for the compiler to actually tell. " is 100% subjective, unless you can PROVE its IMPOSSIBLE for a compiler to tell this claim is completely meaningless.

"i dont like 'static'" there, this statement is just as meaningful. (which is to say it means jack shit) it doesnt tell me anything about what the compiler can/cant/should/should not be able to do.

"without seeing definition of myMagicalFunction" its the compiler.. it CAN see it, it can see ALL your code. how do you think function inlining works? this is basically the same. the question is "is the compiler smart enuff to figure out this expression has this constant value" but no matter what the anwser to that is it doesn't really matter.


"1/0. doesnt throw anything in C or C++" it sets an error code that you should check unless you want your code to do undeterministic crap.. which is just like an exception conceptually except more painful and you will usually forget to do that.

also it is a COMPILE TIME ERROR, you do NOT need to write bloody exception extension for xJASS.. it is the compiler showing a little piece of text and a line number. it is not a bloody language feature so where the fuck are exceptions even related to this?!
 
Level 6
Joined
Jul 30, 2013
Messages
282
So getting a compile error in 10% of the cases just because an optimization step (!!) fails is good and desired behavior?

You suggested that CompiletimeFunction(RuntimeFunction()) is a compile time value. Thats a) not possible for all functions (you would have to treat some runtime functions different than others) and b) limits the programmer - what if I want a new random real in your example every time the function is exectued (which is usually the case)?

. . .
a) not possible for all functions (you would have to treat some runtime functions different than others)

you still dont get what i meant by that? well fine ill write it out the LOOOONG way..
JASS:
global object array mymagicfunctionthatturnseverythingintocompiletimeconstants_stash
function mymagicfunctionthatturnseverythingintocompiletimeconstants takes integer INVOKATION_LOCATION /*inserted by compiler*/, Object value returns Object
    Object val =mymagicfunctionthatturnseverythingintocompiletimeconstants_stash[INVOKATION_LOCATION ]
    if val then // if function has been called from that location.. just return the same value u returned the last time you were called from that point
       return val
    else
mymagicfunctionthatturnseverythingintocompiletimeconstants_stash[INVOKATION_LOCATION ] = value // add to memoize on invokation location
return val
endif
endfunction


if const(random()) then // first call -> INVOKATION_LOCATION  = 0
   st1
endif
if const(random()) then // first call -> INVOKATION_LOCATION  = 1
   st2
endif

if (mymagicfunctionthatturnseverythingintocompiletimeconstants(0, random()) then
   st1
endif

if (mymagicfunctionthatturnseverythingintocompiletimeconstants(1, random()) then
   st2
endif

since mymagicfunctionthatturnseverythingintocompiletimeconstants is memoize
b) limits the programmer - what if I want a new random real in your example every time the function is exectued (which is usually the case)
wtf? its a compile time constant. you NEEEEEEEEEEEEEEEEEEEEEEED a compoile time constant for a static if. having a static keyword does not take this need away in any way and make it magically work.
what if I want a new random real in your example every time the function is exectued. thats why we pass in the index of the invocation the the make-me-a-constant function, so that if u call it in 2 different places you could get 2 different values..
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
that would just try to compile as a regular if and then fail with compile error.

if u used "static if" (GetUnitAbilityLevel) would throw a compile error anyway so this example is MOOT.

Its not, because a compile error must not depend on optimization techniques.

"it would be rather hard for the compiler to actually tell. " is 100% subjective, unless you can PROVE its IMPOSSIBLE for a compiler to tell this claim is completely meaningless.

Proving whether a function is pure or impure (which would be required in the example that edo made) is equivalent to the halting problem and thus, impossible in the general case.



JASS:
global object array mymagicfunctionthatturnseverythingintocompiletimeconstants_stash
function mymagicfunctionthatturnseverythingintocompiletimeconstants takes integer INVOKATION_LOCATION /*inserted by compiler*/, Object value returns Object
    Object val =mymagicfunctionthatturnseverythingintocompiletimeconstants_stash[INVOKATION_LOCATION ]
    if val then // if function has been called from that location.. just return the same value u returned the last time you were called from that point
       return val
    else
mymagicfunctionthatturnseverythingintocompiletimeconstants_stash[INVOKATION_LOCATION ] = value // add to memoize on invokation location
return val
endif
endfunction


if const(random()) then // first call -> INVOKATION_LOCATION  = 0
   st1
endif
if const(random()) then // first call -> INVOKATION_LOCATION  = 1
   st2
endif

if (mymagicfunctionthatturnseverythingintocompiletimeconstants(0, random()) then
   st1
endif

if (mymagicfunctionthatturnseverythingintocompiletimeconstants(1, random()) then
   st2
endif

since mymagicfunctionthatturnseverythingintocompiletimeconstants is memoize

Sorry, but I don't understand anything of what you are trying to say here. Looks like a collection of random ASCII charakters...
 
Level 6
Joined
Jul 30, 2013
Messages
282
Its not, because a compile error must not depend on optimization techniques.



Proving whether a function is pure or impure (which would be required in the example that edo made) is equivalent to the halting problem and thus, impossible in the general case.

the general case maybe.. but you can prove it quite easily in a subset of cases that is easy to define:(haskell is basically built around this so i don't bother prove it too much..)

a function is pure unless its purity is tainted

  • it reads a global mutable variable
  • it reads a file
  • it calls an impure function
  • it has side effects (it prints stuff on screen, writes files, mutates global state)
in this case GetUnitAbilityLevel would be an impure function -> anything calling it is impure. thus the condition is NOT PROVABLY CONSTANT and the compilation should fail.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
the general case maybe.. but you can prove it quite easily in a subset of cases that is easy to define:(haskell is basically built around this so i don't bother prove it too much..)
...
in this case GetUnitAbilityLevel would be an impure function -> anything calling it is impure. thus the condition is NOT PROVABLY CONSTANT and the compilation should fail.

The problem is you don't (want to) understand all the trouble you are getting just because of that. Plus your list is incomplete. And requiring such a list in the first place is bad, because it extremly complicates the language.

Why should the compilation fail at all? Something like


JASS:
scope MyScope initializer onInit
	constant function CheckForTwo takes integer i returns boolean
		return i == 2
	endfunction

	private function onInit takes nothing returns nothing
		if CheckForTwo(GetRandomInt(0, 2)) then
			call BJDebugMsg("two")
		endif
	endfunction
endscope


compiles just fine with the standard if but not with static if. Although the outer function is constant. Btw is a random function also impure, so your example wont even work if there was a method to perfectly determine pureness of a function.

Second, pureness is not the only problem. Whats with functions like

JASS:
if IsPrime(10733) then
    // code
endif

Depending on the implementation of IsPrime it might take (too) long for the compiler to compute (you have to implement a limit). So with this method the compilation would depend on the concrete function (pure or not) and on its input.
 
Level 6
Joined
Jul 30, 2013
Messages
282
"The problem is you don't (want to) understand all the trouble you are getting just because of that." i understand better than you think. you essentially need to parse the function and find all instances when non-local data is referenced in any way, ignore the white-listed cases and if you have any instances left declare the function impure.. i think you severely overestimate the difficulty of solving the issue given the reduced constraints. (we are allowed to label a pure function impure; this is because we can trivially provide a loophole that allows the user override our decision)

"Plus your list is incomplete. And requiring such a list in the first place is bad, because it extremly complicates the language."
you mean calls to natives right? those qualify as reading global mutable state so they are covered. (tho you might be able to white-list a few of those as pure eg I2S is pure also reading global state is OK *if* it is immutable global state )

Why should the compilation fail at all? Something like


JASS:
scope MyScope initializer onInit
	constant function CheckForTwo takes integer i returns boolean
		return i == 2
	endfunction

	private function onInit takes nothing returns nothing
		if CheckForTwo(GetRandomInt(0, 2)) then
			call BJDebugMsg("two")
		endif
	endfunction
endscope

-- i have no idea what this is sopposed to show,
JASS:
call BJDebugMsg("two")
is prefectly fine runtime code so it doesn't have to be static. it compiles to a regular if with 0 issues.

"Btw is a random function also impure"(yes, check out haskell, IO monads for a good overview ),

" so your example wont even work if there was a method to perfectly determine pureness of a function. " and this one is simple nonsense.
JASS:
CheckForTwo(GetRandomInt(0, 2))
is a composite expression composed of sub-exprs.. if any of them is impure the whole expression is impure.
if u are still stuck on me mentioning the pureness of *functions*
JASS:
function Checkfor2OfGetRandomInt takes integer a, integer b, returns boolean
    return CheckForTwo(GetRandomInt(0, 2))
endfunction
there you go, now its determining pureness of function and we know how to do that..


Second, pureness is not the only problem. Whats with functions like

JASS:
if IsPrime(10733) then
    // code
endif

"Depending on the implementation of IsPrime it might take (too) long for the compiler to compute (you have to implement a limit). So with this method the compilation would depend on the concrete function (pure or not) and on its input."

oh.. so its bad to run that at compile time and its OK to have that at runtime?! this is a completely manufactured example with no practical value.
if anything you should calculate IsPrime beforehand so that you dont have runtime issues (lag spikes + risk of operation limit induced thread crashes)

also you may want to look up on how evaluate (with slightly less than 100% confindence) the prime-ness of a number. usually thats enuff and can be done in a very reasonable amount of time. also any crypto lib thats not a piece of junk has one of em built in(for integers thousands of digits long!) so... it definitely wont take too long.


* i wonder which will happen first.. him finally being satesfies or me writing a whole bloody runtime eval() just so prove a point *
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
-- i have no idea what this is sopposed to show,
call BJDebugMsg("two") is prefectly fine runtime code so it doesn't have to be static. it compiles to a regular if with 0 issues.


Lol, I were just citing you, that was your example (and it was as wrong as your Python example):

JASS:
// RandomReal() >0.5 now behaves like a compile-time constant and you cant force the "optimisation", behaves exactly equivalent to true static if, but 0 extra syntax, just a function call.
if ConstantExpression(RandomReal() >0.5) then
    function f..
    endfunction
else

No, RandomeReal does not behave like a compile-time constant. Because its impure. You get that?

Of course you can create a white list where all natives are considered, but thats what I meant with exceptions. It adds massive complexity to the language just because you don't want to differentiate between a preprocessor if and a runtime if, which are completly different things.

i understand better than you think.

I don't think so. Even with your great constraints you still can't determine if a function terminates or not on every possible input. But thats the job of the optimizer anyway.

also you may want to look up on how evaluate (with slightly less than 100% confindence) the prime-ness of a number. usually thats enuff and can be done in a very reasonable amount of time.

Oh really? Imagine, I even wrote a vJass library that does so. Its absolutly not relevant if something can be done efficient. It can also be done in a non-efficient way and then your compiler would break if there are many functions like that.


You are mixing up here compilation, preproccesor and optimization. Of course an optimizer would (and should) transform something like

JASS:
if false then
    call BJDebugMsg("")
endif

such that the whole code block is removed from the compiled script. However thats still not equivalent to a static if. But that doesn't mean this optimization isn't done, its just done by (surprise) the optimizer and not the preprocessor.

There is basically no modern programming language that handels if the way you suggest it (guess why). Several different people gave you numerous detailed explanations why your suggestion would be bad.

However, you ignore all rational arguments while your only argument is: "I want to write if instead of static if, because".
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I will add one example that will do completly something different with your notion of static if, and mine.

JASS:
if true then
    call BJDebugMsg("OHAIDER")
endif

function f takes nothing returns nothing
    
endfunction

The if is compile-time evaluable, yes, but if you treat it as preprocessor if, then you actually outout garbage code, since function call in Jass can not appear outside of function, and here it clearly does. Whereas if you treat it as normal if, you should see that it is a syntax error, since if can not appear outside of function.

Yes, you could add exception for this too, but if you keep adding exceptions like these, then the compiler code gets either spaghetti, or just plain retarded in insaneness
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
usually, you optimize after fully building AST, if you have fully built AST, you pretty much must know whether the if is compiletime or runtime, if you want to treat the code inside differently depending on that.

Im still against if being static with compile-time deducible values. It is both complicating the language, as well as the implementation, and it is also very unintuitive
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
@edo: nice example, I have an even worse one:

@Waffle:

Say a compiler treats

JASS:
if CompiletimeEvaluable() then
    // ...
endif

as static if, then what should it do with this code:

JASS:
if CompiletimeEvaluable() then
    // ...
elseif RuntimeOnlyEvaluable() then
    // ...
endif

So, what now?

Treat the first if branch also as runtime if? Then the meaning of an if block can be changed just by adding an elseif block (which has nothing todo with the if block and is completly independent from it!). Otherwise you would be able to construct if blocks were some parts would be static, others not.

I hope you see now how irredeemably broken such a mechanism would be. It just adds confusion and uncertainty but has zero benefits.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
In that example you posted, if the CompiletimeEvaluable was true, then the first branch would be included and the other branch would be cut off. If it was false, then the other branch is included with an if.

A good optimizer will be able to do this. I do not plan on doing this yet. This will be one of the last things I do -.-.
 
Level 6
Joined
Jul 30, 2013
Messages
282
i give up on looking_for help..

he doesn't even try to think anymore.
a) CompiletimeFunction(RuntimeFunction())
there is no difference between compile-time and runtime functions. u have a function, that's it. u can call it at runtime or compile time there is no difference.

JASS:
// RandomReal() >0.5 now behaves like a compile-time constant and you cant force the "optimisation", behaves exactly equivalent to true static if, but 0 extra syntax, just a function call.
if ConstantExpression(RandomReal() >0.5) then
    function f..
    endfunction
else
"No, RandomeReal does not behave like a compile-time constant. Because its impure. You get that?"

.. are u fking real?
i just explained..
i guess i need explain it better..
you have..
JASS:
int a;
int b;
short c;
c = (short)(a+b); // do maths with a and b, then cast value to short, in reality a and b may take more space than the shot c can fit (depending on context this might be intended or a bad error, thus why the cast is needed)

so why can you accept that we can do:
// those 2 are impure builtings that might have side effects
native function a...
native function b

function c ..
    function purea = ((pure)a)
    function pureb = ((pure)b)
    return purea (pureb ())
endfunction

//this is the exact same thing, for real.
// all that's different is that this, apparrently is beyond some peoples comprehension.
// if u learn maths u leanrn that a = a+1 is nonsense too yet u use it every day now, don't you? (or i++ or what ever your favorite syntax is..)

// in this example ConstantExpression is basically a way of saying cast the expression 
// (in this case "a()") to be treated as if it was a pure expression. 
// yes it is NOT pure, but that does not matter, we as a programmer explicitly
// guarantee to the compiler that is is ok to TREAT IT AS IF IT WAS.
// just as before we guaranteed that its ok to squeeze int + int in to a short we now guarantee that it is ok to treat impure as if it was pure.

// if those int's dont fit in there ther might be overflow and if this was something we did not intend then that could very well fuck up us real bad..
// if we treat impure as pure and yet we rely on some of those impure properties we may well be FUBAR'd.
// but the moment we use a cast / compiler directive / compile hint to express to the compiler our intent we take the responsibility.
// we will reap the rewards if it works and we will pay the price *and the responsibility.. and all the blame* if it doesn't.

ConstantExpression(a())
is basically the same as
((pure) a)()

also.. i still dont get how its a good idea to blame the compiler for YOUR bugs..
if you write a non-halting function then you made a bloody mistake, the mistake is yours and yours alone and you have no bloody right to blame the compiler cuz YOU fucked up and made an infinite loop.. or exceeded the recursion limit.. or used an uninitialized variable or did some other nonsense.
just because we cant prove that a function is computable.. (much less in a reasonable time frame) doesnt mean its nonsense to try and compute it.
a function that never does anything useful is useless. we can kind of expect that you don't put useless crap in your map that does nothing but waste CPU time and memory.

if you really worry.. we can just copy the War3 operation limit (30k bytecode instructions was it? maybe 60k?). would that help? well the compiler could bark at you sooner.. or u could save yourself from having to kill it maybe.. but your code would still be BROKEN. you wrote bloody broken code and it is YOUR fault. stop blaming the compiler man and go fix your bloody code!

for prosperity: i think that the operation limit in warcraft 3 is a DEFICIT that unexpectedly can ruin your code just cuz some dude decided now would be a good time to randomly crash your thread. i think they guy who came up with it should be slapped.. and then he should be slapped a second time for not giving us a way to say we want the op-limit to be ignored.

if anything.. if the compiler can tell you that you exceed the operation limit and throw an error BEFORE you find your map misteriously breaking and wasting hours of your time, that would be a very valuable feature :)

@Nestharus, pretty please add a feature to detect if a function risks reaching the op-limit :)
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
i give up on looking_for help..

Well, I give up on you.
Again you don't have an answer to 90% of the arguments given and just ignore them.


so why can you accept that we can do:
...
// in this example ConstantExpression is basically a way of saying cast the expression
// (in this case "a()") to be treated as if it was a pure expression.
// yes it is NOT pure, but that does not matter, we as a programmer explicitly
// guarantee to the compiler that is is ok to TREAT IT AS IF IT WAS.

As already said thats ridiculus. How should a compiler treat an impure function as if it was pure?? What if the function relies on user input at runtime? Also you never said a word about "casting to pure", you are again inventing some entirely new "syntax" just to defend your position.


JASS:
ConstantExpression(a())
is basically the same as
((pure) a)()

Lol you are really changing your position every second post. May I cite you once more:

JASS:
CheckForTwo(GetRandomInt(0, 2))
is a composite expression composed of sub-exprs.. if any of them is impure the whole expression is impure.

So first you say ConstantExpression(a()) is impure because a subexpression is impure. Now you say the opposite, its a "cast to pure" :D:D:D

You are just as inconsistent as you compiler suggestions.

I end this discussion now, its pointless. You ignore all arguments, thats just trolling.




For the sake of completness:

also.. i still dont get how its a good idea to blame the compiler for YOUR bugs..
if you write a non-halting function then you made a bloody mistake, the mistake is yours and yours alone and you have no bloody right to blame the compiler cuz YOU fucked up and made an infinite loop

is of course wrong. You didn't understand anything about the halting problem / the problems one has to face when building a compiler. There is a huge difference between a function thats always non-halting (which is what you imply here and of course makes no sense) and a function for which it is not possible to prove whether it is halting or not (which means: in practise the function halts, but you are not able to prove that during the compilation/optimization phase).

Even for very simple functions with just a loop and few arithmetic operations it can be arbitrary hard (or even impossible) to prove if the function halts. You can't cover that neither with whitelists nor with your constraints.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, as there is still some serious disagreement about the type system of JASS.

This is the reason why I will not use classes or structs. JASS already has a type system, and it uses the "type" keyword. This type system already has things like inheritance and implicit typecasting as well. It does not make sense to add a second type system. This would just muddle the language and make it messy. I have absolutely no intention of muddling it.

The primary challenge of using the JASS type is that it is a one liner. In order to use both standard JASS and xJASS together (JASS must be a subset of xJASS), then type somehow allow a block.

I now realize that splitting the scope up from the type is a bad idea. This changes the meaning of scope depending on whether a type with that same name was declared or not.

The expand keyword was introduced to state that we are expanding the type out into a scope. Scope doesn't make sense as a decorator unless it is applied to everything and can be used on its own, similarly to preprocessor. As function has a scope without the scope keyword, we can't do this.

One thing I kind of wanted to be able to do was nest types. However, if we don't nest types, then we can do something extremely smart with the compiler and allow for

JASS:
type a extends integer
endtype

If we encounter an endtype somewhere, then that was a block. If we don't or we encounter another type, then it was a simple type declaration. We can use operators to sort of nest types together, like we do in vJASS. As it doesn't seem like any other option is a good one, I'm of the opinion that types should not be able to be nested, thus we end up with just a simple type/endtype block.

We should also have this pretty cool thing

JASS:
type a extends integer
    public function this.m takes nothing returns nothing
    endfunction
endtype

scope d
    public function m takes nothing returns nothing
    endfunction
endscope

type b extends unit
    // imports from a
    public importer function this.a takes nothing returns a
        return GetUnitUserData(this)
    endfunction

    // importer functions aren't included as traditional symbols
    public function this.a takes nothing returns nothing
    endfunction

    // imports from d
    public importer function d takes nothing returns nothing
    endfunction
endtype

b bb = new b(...)
call bb.m() // cool? : D
call b.m()

This works similarly to delegates from vJASS except that it allows you to map to other types any way you like. Most importers will be optimized away or not used at all.


The only thing we'd seriously be missing from vJASS features is keyword, which we won't need with internal ^)^.
 
Level 6
Joined
Jul 30, 2013
Messages
282
Ok, as there is still some serious disagreement about the type system of JASS.

This is the reason why I will not use classes or structs. JASS already has a type system, and it uses the "type" keyword. This type system already has things like inheritance and implicit typecasting as well. It does not make sense to add a second type system. This would just muddle the language and make it messy. I have absolutely no intention of muddling it.

The primary challenge of using the JASS type is that it is a one liner. In order to use both standard JASS and xJASS together (JASS must be a subset of xJASS), then type somehow allow a block.

I now realize that splitting the scope up from the type is a bad idea. This changes the meaning of scope depending on whether a type with that same name was declared or not.

The expand keyword was introduced to state that we are expanding the type out into a scope. Scope doesn't make sense as a decorator unless it is applied to everything and can be used on its own, similarly to preprocessor. As function has a scope without the scope keyword, we can't do this.

One thing I kind of wanted to be able to do was nest types. However, if we don't nest types, then we can do something extremely smart with the compiler and allow for

JASS:
type a extends integer
endtype

If we encounter an endtype somewhere, then that was a block. If we don't or we encounter another type, then it was a simple type declaration. We can use operators to sort of nest types together, like we do in vJASS. As it doesn't seem like any other option is a good one, I'm of the opinion that types should not be able to be nested, thus we end up with just a simple type/endtype block.

We should also have this pretty cool thing

JASS:
type a extends integer
    public function this.m takes nothing returns nothing
    endfunction
endtype

scope d
    public function m takes nothing returns nothing
    endfunction
endscope

type b extends unit
    // imports from a
    public importer function this.a takes nothing returns a
        return GetUnitUserData(this)
    endfunction

    // importer functions aren't included as traditional symbols
    public function this.a takes nothing returns nothing
    endfunction

    // imports from d
    public importer function d takes nothing returns nothing
    endfunction
endtype

b bb = new b(...)
call bb.m() // cool? : D
call b.m()

This works similarly to delegates from vJASS except that it allows you to map to other types any way you like. Most importers will be optimized away or not used at all.


The only thing we'd seriously be missing from vJASS features is keyword, which we won't need with internal ^)^.

this 5th option..
i find this acceptable, +1 :)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Ok, as there is still some serious disagreement about the type system of JASS.

This is the reason why I will not use classes or structs. JASS already has a type system, and it uses the "type" keyword. This type system already has things like inheritance and implicit typecasting as well. It does not make sense to add a second type system. This would just muddle the language and make it messy. I have absolutely no intention of muddling it.

The primary challenge of using the JASS type is that it is a one liner. In order to use both standard JASS and xJASS together (JASS must be a subset of xJASS), then type somehow allow a block.

One suggestion:

Making Jass a subset of xJass makes sense and is good. But: the type keyword isn't really a full member of the Jass language.

Yes, it is part of the final map script (as in common.j), but map makers are not allowed to use it in a script. If I put type a extends integer into a vanilla Jass script it throws a compile error. So its not a part of usable Jass code.

Therefore I would suggest to use struct/class as thats most convenient and remove the type keyword (or change how it works, in vJass its basically just a typedef). Then you directly avoid problems with the one-liner type declaration.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
ah ok...seems the vanilla editor will reject it, but it should compile with JassHelper :D

Its still utterly useless

Yes, with JH it works. But there can only be used like a typedef

JASS:
type int extends integer

function foo takes nothing returns nothing
	local int i = 0 // compiles in JH
	call BJDebugMsg(I2S(i + 2))
endfunction

However, for a typedef the syntax is awful, so it should be changed anyway.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
I looked at that video and I don't see how it helps? : P

Then i have good news for you: the talk is free to watch so you can watch it as many times as it needs. In fact all should watch it at least twice.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
Advanced optimizer features. Such as compile time constant merging, automatic name-shortening, automatic in-lining of small functions, automatic splitting of arrays with only constant index access (such as used by a lot of GUI users) into separate variables.

Language was the ability to initialize structure (struct or class) instances and arrays from an initializer list would be useful.

Constant array and structure support should also be added where an initializer list can be used to initialize them as a constant object. Both of these are important as they allow for more compile time optimizations (such as in-lining array values or structure member values to avoid an array lookup operation). Physically they need not be constant as far as implementation goes however it should throw a compile error if you try to modify them.

A special destructive loop flow control block (eg "dfor") for use on group and force objects. This is different from normal "for" loops in that they are not destructive. This would allow easy use of such an optimization without exposing the standard "for" loop to unusual or unclear behaviour.

Support for non-native primitive operators. These include bitwise operators as well as mathematical operators. The implementation is software emulated (slow) and designed to take advantage of inlining when appropriate (for maximum speed).

Automatic clean-up of GUI garbage (more efficient GUI compiler). This importantly includes the substitution of broken GUI functions (eg PolledWaitBJ) with a fixed implementation. Could possibly be built into the optimization process to be done automatically rather than having to specifically parse GUI.

Support for instrumentation with some form of in-game debugger possibly. Speed is unimportant here (one can expect it to be slow).
 
Level 6
Joined
Jul 30, 2013
Messages
282
-1 for dfor
why?:
  • most people will use "for"
  • dfor is another keyword, another structure
  • people will try for just because it works in other languages (for elem in iterable or sth like that would be something i presume would exist in all modernt languages) i have never heard of dfor in any language, in face if i didnt know this refers to the cleaning up of the temporary var id be scared off right now and many noobs will be.
  • dfor is ugly (last.. and least)

i think for should iterate over a collection, it should not deal with creating/destroying anything. (well user visibly, it is ok to create all sorts of stuff as long as you clean it up, it is NOT ok to create temp stuff and leave it hanging around and force the user to use some special keyword to direct the cleanup)

+1 for "Support for non-native primitive operators. These include bitwise operators as well as mathematical operators. The implementation is software emulated (slow) and designed to take advantage of inlining when appropriate (for maximum speed)."


oh and.. thx for the link to the Guy Steele talk :)
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,201
•most people will use "for"
•dfor is another keyword, another structure
•people will try for just because it works in other languages (for elem in iterable or sth like that would be something i presume would exist in all modernt languages) i have never heard of dfor in any language, in face if i didnt know this refers to the cleaning up of the temporary var id be scared off right now and many noobs will be.
•dfor is ugly (last.. and least)
Still is better than a "for" block with non-standard behaviour. Some compilers have done this in the past. It being non-standard is because its use case is non-standard. One must sort of remember that this is a specialist programming language (only aimed at WC3) so introducing new and special key words for specific purposes is reasonable.

Either that or the compiler is built to automatically optimize out situations where a destructive for loop can be used (group which is iterated through for once and then destroyed/refilled).
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I dont understand what you mean by the term "destructive" loop, but you can basically write

C++:
for(unit u = FirstOfGroup(g); u != null; u = FirstOfGroup(g))
{
    //do stuff

    call GroupRemoveUnit(g, u)
}

and you basically achieved the same thing. With good enough language, you could move GroupRemoveUnit up to the stepper
 
Level 6
Joined
Jul 30, 2013
Messages
282
i despise that syntax..
i know so many ppl are used to the c-style for.. but it SUCKS. if you write that u can just as well write loop/endloop and exitwhen..; that IS what you are writing now its just arranged at different places..

i strongly would like to request syntax like

for unit u in group
// use u..
endfor

or...
for unit u in FirstOfGroupIterator(group) // using an explicit iterator would be a semi-elegant way to select if u want to destroy the group or not etc..

"Still is better than a "for" block with non-standard behaviour. Some compilers have done this in the past. It being non-standard is because its use case is non-standard. One must sort of remember that this is a specialist programming language (only aimed at WC3) so introducing new and special key words for specific purposes is reasonable."

from the sound of that it sounds like destroying the group would be "standard" behaviour? i most strongly disagree.
if you write

JASS:
// your standard c-like syntax..
for (int i = 0; i<a.length;++i){
   printf(a);
}
you do NOT expect a to be empty afterwards!
similarly 
// whaty you'd do in Java
for (element e:mylist){}
//python
for e in list:
    pass
//javascript
for(key in object){}

// im sure you notice the common property that given a data structure it is NOT destroyed. no list, array , dict was harmed during the making of this post..
 
Last edited:
Status
Not open for further replies.
Top