wurstscript / WurstScript

Programming language and toolkit to create Warcraft III Maps
https://wurstlang.org
Apache License 2.0
224 stars 30 forks source link

Add a way to use compiletime and runtime actions in the same function. #365

Closed peq closed 9 years ago

peq commented 9 years ago

I am not yet sure how this could be done. Some discussion is below:

(15:45:35) Cokemonkey11: peq, why can't I create a unit in a compiletime function? (15:46:05) peq: you mean a new unit type? (15:46:09) peq: or a real unit? (15:46:31) peq: the first one should be possible (15:49:49) Cokemonkey11: i mean createunit() (15:50:14) Cokemonkey11: it would be a nice feature if compiletime functions could automaticaly detect non-compiletime events (15:50:21) Cokemonkey11: and build initialization functions instead (15:50:38) Cokemonkey11: because it would be useful for example i have a unit which is only for decoration (15:50:49) Cokemonkey11: i compiletime initialize it and then place it in the same function (15:55:32) WaterKnight: how are comptiletime functions scheduled (16:02:34) ***Cokemonkey11 slaps peq around a bit with a large fishbot (16:04:04) peq: hm, I think we could do that (16:04:12) peq: but I don't see much benefit in it (16:04:23) peq: you can do the same thing in normal code (16:05:17) Cokemonkey11: yeah its just a pain in the ass (16:05:21) Cokemonkey11: if i have a compile time function for 10 units (16:05:27) Cokemonkey11: and also a single placement for each of those 10 units (16:05:39) Cokemonkey11: it isn't very pragmatic to have a big set of function pairs (16:06:49) peq: one function to generate the objects + one init block (16:07:22) Cokemonkey11: right but the data is associated regardless (16:07:28) Cokemonkey11: i have 10 units that each need to be placed once (16:08:21) peq: I think it would be confusing to mix compiletime and runtime like this (16:08:55) peq: then you have to know for each function which one belongs to compiletime and which to runtime (16:09:21) Cokemonkey11: just remove @ compiletime completely (16:09:31) Cokemonkey11: compiletime events should be implicit (16:09:44) Cokemonkey11: functions should support both types of event (16:09:49) Cokemonkey11: and the compiler should arbitrate that (16:10:13) peq: functions can be used in both types (16:10:23) peq: the annotation is just to say which ones are called (16:10:52) peq: it wold not make sense to just call all the functions in the script (16:11:40) Cokemonkey11: just merge compiletime and initialization functions (16:11:48) Cokemonkey11: initialization can never perform compiletime events (16:11:54) Cokemonkey11: and compiletime events can never be run during initialization (16:12:04) Cokemonkey11: so the distinction is just overly verbose (16:12:37) peq: so you want to write compiletime code in normal init blocks? (16:12:58) peq: and the compilere should check the contents of init blocks to see which ones should be run at compiletime? (16:13:10) Cokemonkey11: i want to be able to write good functional code that takes a set of data (unit type and location) and both initialized and instantiates them (16:13:19) Cokemonkey11: instead of writing two functions to perform half of those actions on the same data (16:13:26) Cokemonkey11: yes exactly (16:14:45) peq: hmm (16:15:24) peq: someone has done something like this by using if blocks (16:15:30) peq: maybe it was Crigges (16:15:35) peq: something like (16:15:45) Cokemonkey11: if blocks? what do you mean (16:15:48) Cokemonkey11: i dont understand how it can be (16:15:58) peq: if compiletime then else (16:16:31) Cokemonkey11: how is that possible (16:17:29) peq: https://github.com/Crigges/HotN/blob/080bce449d51e9d63e8d4011292586935031e011/HotN/wurst/helper/CompiletimeInit.wurst (16:17:29) Cokemonkey11: is the @ compiletime annotation just an alias for something else? (16:18:13) Cokemonkey11: that.. that is spagetti (16:18:30) peq: and https://github.com/Crigges/HotN/blob/080bce449d51e9d63e8d4011292586935031e011/HotN/wurst/OeSetup.wurst#L26 (16:20:16) Cokemonkey11: that is useful (16:20:26) lep: kek (16:20:28) Cokemonkey11: i think that this is a language smell though (16:21:02) WaterKnight left the room (quit: Quit). (16:22:18) peq: i don't like it much (16:23:42) Cokemonkey11: what don't you like, the second example? it's just an ugly version of my suggestion (16:23:45) Cokemonkey11: still better than two functions though (16:24:06) peq: it's both one example (16:24:20) peq: the first is to set the "compiletime" variable (16:24:24) peq: that is a bit ugly (16:24:40) peq: would be nicer if there was a builtin constant for that (16:25:08) peq: and I also don't like the mixing of compiletime and runtime (16:25:29) peq: I think it even depends on the optimizer to eliminate the if-statements (16:29:30) Cokemonkey11: he shouldnt have to do that imo (16:29:43) Cokemonkey11: it should be part of the compiler to do compiletime actions during compile time (16:29:48) Cokemonkey11: and everything else during map load (16:30:04) Cokemonkey11: unless you have really good example of an action that has a good reason to run in both (16:32:01) peq: usually there are some functions shared by both (16:32:15) peq: but the actions are separate (16:35:09) Cokemonkey11: right, some actions can occur in either case, that's fair (16:35:30) Cokemonkey11: actually, which actions are those? (16:36:09) peq: setting up the data (16:37:17) peq: so data which you need to create the objects and which you also need at runtime (16:41:57) Cokemonkey11: such values should be constants (16:42:04) Cokemonkey11: or equivalent for both (16:42:05) Cokemonkey11: no? (16:42:22) peq: yes (16:42:37) Cokemonkey11: so the compiler could just duplicate this part of the code (16:42:39) Cokemonkey11: in the generated output (16:43:10) peq: but how should it know which parts are required by the runtime? (16:43:16) peq: and which by the compiletime? (16:45:15) Cokemonkey11: for example, each initialization function could be parsed twice - once as though it were compiletime, and once as though it were initialization time (16:45:45) Cokemonkey11: actions which have effect on the compiletime one are stripped in the initialization time one (16:45:54) Cokemonkey11: any extra code like conditions and loops would be stripped automatically (16:46:13) Cokemonkey11: once that compiletime stuff is gone in the initialization pass that is stripped of compile-time only actions (16:46:27) Cokemonkey11: im sorry im not very good at describing this, i never wrote a compiler before

peq commented 9 years ago

Here would be an example using a special annotation @withRuntime. The meaning of this would be that all calls to native functions, which are not understood by the compiletime interpreter, are recorded and added to map initialization time.

@compiletime @withRuntime
function example()
    for i = 1 to 8
        new ItemDefinition('I000' + i, 'Ibla')
            ..setName("Tome of attack " + i.toString())
        createItem('I000' + i, vec2(0., 0. + 128. * i))

// would generate:
init
    CreateItem(1227894833, 0.0, 128.0)
    CreateItem(1227894834, 0.0, 256.0)
    CreateItem(1227894835, 0.0, 384.0)
    CreateItem(1227894836, 0.0, 512.0)
    CreateItem(1227894837, 0.0, 640.0)
    CreateItem(1227894838, 0.0, 768.0)
    CreateItem(1227894839, 0.0, 896.0)
    CreateItem(1227894840, 0.0, 1024.0)

Open questions:

@compiletime @withRuntime
function example_problem()
    for i = 1 to 8
        new ItemDefinition('I000' + i, 'Ibla')
            ..setName("Tome of attack " + i.toString())
        createItem('I000' + i, GetPlayableMapRect().getCenter().polarOffset(angle(0), 128.*i))

or even:

for i = 1 to GetPlayers()
    ...

My first idea would be too throw an error if a return value from such a function is used somewhere, but that is probably too restrictive. If we make it more flexible, it would be very hard to explain the semantics of this construct, I guess.

Cokemonkey11 commented 9 years ago
@compiletime @withRuntime
function example_problem()
    for i = 1 to 8
        new ItemDefinition('I000' + i, 'Ibla')
            ..setName("Tome of attack " + i.toString())
        createItem('I000' + i, GetPlayableMapRect().getCenter().polarOffset(angle(0), 128.*i))

There is no reason why non-compiletime init blocks can't use natives, so this example is equivalent to two functions, generated by a preparsing stage:

@compiletime
function example_problem_gen()
    for i = 1 to 8
        new ItemDefinition('I000' + i, 'Ibla')
            ..setName("Tome of attack " + i.toString())

init
    for i = 1 to 8
        createItem('I000' + i, GetPlayableMapRect().getCenter().polarOffset(angle(0), 128.*i))
for i = 1 to GetPlayers()
    ...

There are a couple ways to deal with this:

Thus, an example with such a call whose contents contain both compiletime and initialization level code, should error.

I'm not a big fan of this choice because it could cause unintentional collisions for users who expect GetPlayers() to behave as their native. Consider a map with 6 players and they want to organise groups of items in 3 sets:

@compiletime
fn what_happens_here?()
    names = ['Bob', 'Joe', 'Bill', 'Skooma', 'Peq', 'Frotty']
    types = ['Sword', 'Shield', 'Helmet']

    for i = 1 to GetPlayers()
        new ItemDefinition('I000' + i, 'Ibla')
            ..setName(types[0] + ' of ' + names.sample)
        new ItemDefinition('I007' + i, 'Iblb')
            ..setName(types[1] + ' of ' + names.sample)
        new ItemDefinition('I00E' + i, 'Iblc')
            ..setName(types[2] + ' of ' + names.sample)

Then you will have a collision if GetPlayers() in compiletime resolves to 12 or 15 - and this could be a potentially difficult bug to solve.

It also sets a bad precedent if other natives require special cases - better just raise error.

  1. Often one wants init code to be executed delayed, so that load time is not affected and the maximum number of instructions are not a problem in the init. How could this be done here?

This is an earlier stage in init block processing. The init block generated by @withRuntime blocks should get fed into the optimiser.

peq commented 9 years ago

So the compiler would basically do this:

  1. split the function into a runtime and compiletime version
  2. in the compiletime function: ignore all native functions unknown to the interpreter. Give a compilation error if the result of such a function is used somewhere else.
  3. in the runtime function: replace all calls to compiletime-natives with do-nothing stubs. Then hope that the optimizer will throw out all the unnecessary code or live with the garbage code.

I think that would not be too much work to implement, but I still think the behaviour would be a bit weird.

This is an earlier stage in init block processing. The init block generated by @withRuntime blocks should get fed into the optimiser.

The optimizer cannot (and should not) move initialization to a later phase.

Cokemonkey11 commented 9 years ago

So the compiler would basically do this

Yes, I think something like this is what I'm getting at.

Side note: part of our discussion was that I was suggesting removing init blocks in favor of using annotations.

Another option would be to just remove the @compiletime annotation, and make all init blocks valid for @withruntime.

The optimizer cannot (and should not) move initialization to a later phase.

What I was talking about here is related to the problem you said:

Often one wants init code to be executed delayed, so that load time is not affected and the maximum number of instructions are not a problem in the init. How could this be done here?

Cokemonkey11 commented 9 years ago

Any news on this?

peq commented 9 years ago

So Crigges tested that the compiletime function works. Meaning you can now do something like this:

init
    doInit()

@compiletime
function doInit()
    for i = 1 to 100
        if compiletime
            // create item object
        else
            // place item on map

I think this is enough for this feature so I am closing this ticket.

I am also adding the discussion from IRC today, as it might be interesting for other readers:

(12:27:15) Cokemonkey11: peq, hi (12:27:31) peq: hi (12:27:35) Cokemonkey11: i still want compiletime functions and init blocks in the same scope :P (12:27:50) peq: got it :) (12:28:43) Cokemonkey11: is that a positive or a negative response... (12:29:16) peq: depends (12:29:35) peq: I will add a constant so that you can use "if compiletime ...." (12:29:45) peq: but probably not the other things (12:33:04) Cokemonkey11: peq that is so nasty... (12:33:09) Cokemonkey11: you must agree that this is a language smell (12:33:32) WaterKnight: how are compiletime funcs mixed with init funcs (12:33:39) peq: no (12:33:45) peq: I don't agree (12:33:56) peq: mixing two concepts would be confusing and a language smell (12:34:17) WaterKnight: remember to clean your wurst (12:34:36) Frotty [~frottyzd@ip-178-200-132-42.hsi07.unitymediagroup.de] entered the room. (12:34:36) mode (+o Frotty) by Q (12:35:30) Cokemonkey11: WaterKnight, compiletime actions should unambiguously run during compile time. Other actions should unambiguously not run during compiletime. (12:35:42) Cokemonkey11: Adding language features to describe the two kinds of action is a language smell (12:35:55) Cokemonkey11: The init block is already a language smell (12:36:10) WaterKnight: [12:35] WaterKnight, compiletime actions should unambiguously run during compile time. Other actions should unambiguously not run during compiletime. (12:36:19) WaterKnight: which means init should be disconnected from compiletime (12:36:37) Cokemonkey11: No, because the actions that run in init and compiletime could never be confused for belonging in the other (12:36:47) Cokemonkey11: So it is worthless (no information) metadata (12:37:17) Cokemonkey11: if you say CreateUnit() in a compiletime function, it is clear that this can only run during initialization (12:37:35) WaterKnight: is that cler? (12:37:37) WaterKnight: clear (12:37:37) Cokemonkey11: if you do something with objutils during initialization, it is clear that this can only be run during compiletime (12:37:38) Cokemonkey11: yes (12:37:49) WaterKnight: only because you know the createunit function (12:38:12) Cokemonkey11: it isnt interesting whether I know it or not (12:38:18) Cokemonkey11: from the developer and in-game perspective, the behavior is not changed (12:38:46) WaterKnight: im not too fond of static initialization anyway (12:39:04) Cokemonkey11: I don't know what you mean (12:39:12) WaterKnight: but there are language features to maintain readability (12:39:22) WaterKnight: do you only see in this instance (12:39:33) WaterKnight: [12:35] Adding language features to describe the two kinds of action is a language smell (12:40:06) WaterKnight: you are basically saying you should only insert the minimal amount of keywords? (12:40:12) WaterKnight: and have everything context based? (12:40:49) Cokemonkey11: No, some level of metadata is good. But I think having an annotation for compiletime and a keyword for init is silly (12:41:02) Cokemonkey11: there should be one annotation that does both, and init should be gone (12:41:09) Cokemonkey11: or deprocated... (12:41:12) WaterKnight: what's the benefit of connecting it? (12:42:03) WaterKnight: when you connect it you would assume a correlation (12:42:34) WaterKnight: but i actually thought in wurst it was already the case (12:42:42) WaterKnight: that you can write compiletime calls in init (12:43:28) Cokemonkey11: the benefit in connecting it is every case where you might want to instantiate an object immediately after creating it. in particular for arrays of objects (12:43:43) Cokemonkey11: Even if compiletime was allowed in init, I'd probably complain, because packages can have only one init block (12:44:08) peq: you can have many init blocks (12:44:24) peq: and you can still use both functions for both (12:44:30) peq: I'll make an example (12:44:44) Cokemonkey11: Okay, if you can have compiletime actions in init blocks (12:44:49) WaterKnight: are compiletime funcs limited to object creation? (12:44:50) Cokemonkey11: then I don't see the point of @compiletime annotation (12:46:00) peq: init functions are not run at compiletime (12:46:18) peq: but you can do something like this: (12:46:18) peq: http://peeeq.de/code.php?id=9021 (12:47:35) WaterKnight: so basically doInit is run twice but on compiletime only the compile instructions and ingame only the init instructions (12:47:56) peq: yes (12:48:03) peq: and you can have some code that runs in both (12:48:08) peq: like the loop in my example (12:48:38) Cokemonkey11: peq, is this already allowed? (12:48:45) peq: it's just that I would do it explicitly with "if compiletime" and Cokemonkey wants the compiler to figure out which parts belong to compiletime (12:48:48) Cokemonkey11: it's hacky and ugly as fuck, but better than nothing (12:49:04) peq: currently the "compiletime" constant does not exist (12:49:20) Cokemonkey11: peq, every action that happens in compiletime can never run in game time (12:49:20) peq: so you have to do something like Crigges did: https://github.com/Crigges/HotN/blob/080bce449d51e9d63e8d4011292586935031e011/HotN/wurst/helper/CompiletimeInit.wurst (12:49:33) Crigges: hello (12:49:39) WaterKnight: is the idea that you do not distinguish that wc3 actually cannot create object data etc ingame (12:49:50) WaterKnight: you pretend it could (12:49:52) Cokemonkey11: no the idea is that you should write nice looking code (12:49:52) WaterKnight: at init (12:50:01) WaterKnight: i mean peq (12:50:20) peq: you mean what I understnad of cokes idea? (12:50:31) WaterKnight: you pretend that you make up the map just in time (12:50:43) peq: no (12:50:57) peq: you just share some code (12:51:11) peq: and some people prefer to mix compiletime and runtime in the same function (12:51:27) peq: which would require this "compiletime" constant (12:51:28) WaterKnight: yes i mean for readability (12:51:36) WaterKnight: what was the initiative at mixing those (12:52:13) Cokemonkey11: http://peeeq.de/code.php?id=9022 (12:52:23) Cokemonkey11: functional programming was the initiative (12:52:38) WaterKnight: you can basically read it like "here's the map init, let's insert this peasant unit in the data base, then use it" (12:52:45) WaterKnight: though the order probably isnt maintained (12:52:51) Crigges: urg (12:53:26) Crigges: so the compiler should decide if stuff is compiletime or not? (12:53:30) Cokemonkey11: absolutely (12:53:38) Cokemonkey11: because compiletime functions are always compiletime (12:53:42) Cokemonkey11: native functions are never compiletime (12:54:05) Crigges: createObject(someFunction(someOtherFunction())) (12:54:12) WaterKnight: i am not exactly for using the default init (12:54:19) peq: I already gave this counter example: http://peeeq.de/code.php?id=9023 (12:54:29) peq: this should give an error (12:54:38) Cokemonkey11: compiler error (12:54:49) Cokemonkey11: getNumberOfPlayers() could not be validated for compiletime function (12:54:49) peq: but with your approach it would just ignore the call to getNumberOfPlayers() (12:55:07) WaterKnight: the default init does not let you customize things as much (12:55:20) WaterKnight: as crigges experienced with units and abilities (12:55:54) Crigges: Cokemonkey11: the compiler can't and should not decide what is executed @runtime and @compiletime (12:56:22) Crigges: another big issue are the ids (12:56:43) Crigges: createObject(genId()) (12:57:10) Crigges: this would change the runtime behavior (12:57:17) Crigges: if you use some of the ids (12:57:17) Cokemonkey11: i dont know anything about ids (12:57:30) Cokemonkey11: but i disagree that just because this is a hard problem to solve, doesnt mean the compiler should not do it (12:57:40) Crigges: it is not hard (12:57:44) Crigges: it is impossible (12:57:52) Cokemonkey11: i dont believe you lol (12:57:57) peq: it is not hard, it would be stupid to solve it (12:58:05) peq: :D (12:58:12) Cokemonkey11: nah you're wrong (12:58:23) peq: we just have different taste (12:59:04) WaterKnight: are you emphasizing (12:59:07) WaterKnight: the getnumberofplayers (12:59:20) WaterKnight: that the function has to return a value for both compiletime and ingame (12:59:26) WaterKnight: or that it may differ (12:59:48) Cokemonkey11: I'm emphasizing that using a getNumberOfPlayers() loop with compiletime actions inside should be a compiletime error (13:00:07) Cokemonkey11: just because the compiler figures out what happens in compiletime (13:00:14) Cokemonkey11: doesn't mean users shouldnt know what happens in compiletime (13:00:18) WaterKnight: what does getnumberofplayers do (13:00:21) Cokemonkey11: it only means they shouldn't have to provide the compiler with that metadata (13:00:41) Cokemonkey11: lets pretend it returns the number of players controlled by user or computer (13:00:50) peq: getnumberofplayers was supposed to be an example of something you only know at runtime (13:01:11) WaterKnight: yeah (13:01:27) WaterKnight: you cannot return a value you only get at runtime (13:01:39) Cokemonkey11: so its clearly a compiletime error (13:01:44) Cokemonkey11: i cant believe you guys are against his (13:01:46) Cokemonkey11: this* (13:01:56) WaterKnight: im not against anything yet (13:02:11) WaterKnight: but the interests and concerns are not clear yet (13:02:34) peq: so why would this be a compiletime error but "unit u = CreateUnit(....) " would not? (13:02:58) WaterKnight: would getnumberofplayers have a 2nd channel for runtime then? (13:02:58) peq: the unit you get is also just available at runtime (13:03:05) WaterKnight: does it need to be marked as compiletimefunc? (13:03:06) Crigges: http://peeeq.de/code.php?id=9024 so how to solve this one? (13:03:22) Crigges: i can give you a lot of other natives (13:03:39) Crigges: which COULD be used @compiletime (13:04:06) Cokemonkey11: Crigges, I'm happy to let idiots write that and have it not work (13:04:09) Cokemonkey11: XD (13:04:23) Cokemonkey11: show me real code that is valid (13:04:31) Crigges: woot? (13:04:33) Cokemonkey11: and doesnt have obvious collision issues (13:04:56) Crigges: okay (13:05:04) peq: that is a really good example (13:05:04) Cokemonkey11: my official answer: it should be a compiletime error because GetRandomInt() shouldnt be used at compiletime (13:05:16) peq: it looks like it would use the same id in both cases (13:05:22) peq: but does something different (13:05:22) Cokemonkey11: it looks like shit (13:05:24) Cokemonkey11: no one would write that (13:05:39) WaterKnight: can compiletime even rely on the blizz funcs? (13:05:54) peq: some functions are implemented for compiletime (13:05:56) WaterKnight: i mean even if it is a deterministic function (13:06:52) WaterKnight: seems like most would have to be constant (13:07:14) WaterKnight: the genid function may be special (13:07:15) peq: it's mainly math and conversion functions that are implemented (13:07:22) Crigges: http://peeeq.de/code.php?id=9025 (13:07:38) Cokemonkey11: Crigges, I already thought of this (13:07:42) Cokemonkey11: this is a much better example (13:07:48) Cokemonkey11: Idk what to say yet :D (13:08:52) Cokemonkey11: I'm going to the gym and I will think about this, but right now I think this example is The Nail In The Coffin (13:08:58) Cokemonkey11: I still want if compiletime though (13:09:20) Cokemonkey11: this is definitely solvable (13:09:26) Cokemonkey11: but not anymore reasonable (13:09:29) WaterKnight: i guess the problem is that you dont see it from outside (13:09:31) Cokemonkey11: now it's complicated (13:09:35) Crigges: i did a lot of compiletime stuff and there are nice ways to solve this without chaning wurst (13:09:42) Cokemonkey11: solving yes (13:09:45) Cokemonkey11: writing nice code, no (13:09:56) Cokemonkey11: I saw some of your examples and I wanted to stop writing wurst (13:10:06) Cokemonkey11: too much indirection (13:10:31) WaterKnight: i am not sure if you should have compiletime funcs in the code (13:10:49) WaterKnight: since i am planning atm an object editor (13:12:01) WaterKnight: the idea there is more like you set yourself scopes and may statically or by script create the objects (13:12:07) WaterKnight: but not directly in the mapscript (13:12:09) Crigges: okay why this code isn't nice? (13:12:14) Cokemonkey11: wait (13:12:15) Cokemonkey11: im still here (13:12:15) WaterKnight: and then that's like catalogues (13:12:21) Cokemonkey11: what if you can just say (13:12:29) Cokemonkey11: if you use @initcompile (13:12:43) Cokemonkey11: that, any variable that is used by compiletime action (13:12:50) Cokemonkey11: must not have been affected by a function that has side-effects (13:12:57) Cokemonkey11: that solves the problem and would be easier to implement (13:13:01) Cokemonkey11: but might be hard to explain in a tutorial (13:13:35) WaterKnight: but that restricts a lot (13:13:39) Cokemonkey11: i.e. if side-effects change a variable and a compiletime action uses that variable: compiletime error (13:13:39) Crigges: Cokemonkey11: what is better? You decide yourself what is executet @compiltime and what is executet @runtime OR the compiler solves this problem and you don't know what exactly happens? (13:13:57) Cokemonkey11: Crigges, I'm not suggesting we remove @compiletime annotation (13:14:05) Crigges: i know (13:14:28) WaterKnight: when you cannot have side effects, you basically have to disconnect runtime and compiletime again (13:14:30) Cokemonkey11: I think you should let good programmers write code that will clearly do what they want during both phases (13:14:40) Crigges: you want to combine compiletime AND runtime (13:14:45) Cokemonkey11: I'm happy to disconnect them - not happy to write 2 functions (13:14:55) Crigges: and the compiler should seperate this stuff (13:15:05) Cokemonkey11: with the @compileinit annotation, yes! (13:15:14) Crigges: you don't have to (13:15:22) Cokemonkey11: I don't even need the compiletime errors (13:15:25) Crigges: just create your own annotation (13:15:29) Crigges: and use (13:15:32) Crigges: if compiletime (13:15:37) Cokemonkey11: if compiletime doesnt exis (13:15:42) Crigges: use my package (13:15:52) Crigges: https://github.com/Crigges/HotN/blob/080bce449d51e9d63e8d4011292586935031e011/HotN/wurst/helper/CompiletimeInit.wurst (13:15:53) Cokemonkey11: put it in the standard library and use it (13:16:06) Crigges: and change dinit to compileinit (13:16:12) Crigges: and youve got what you want (13:16:23) Crigges: https://github.com/Crigges/HotN/blob/master/HotN/wurst/Heros/SampleHero/SampleHero.wurst (13:16:53) Crigges: this code runs @compile and @runtime (13:17:15) Crigges: and the class behind decides what to do (13:17:19) WaterKnight: we need more times (13:17:47) Cokemonkey11: I'm basically saying (13:17:49) Cokemonkey11: I want to use this code (13:17:53) Cokemonkey11: but I don't want this code in my map (13:17:54) Cokemonkey11: LOL (13:18:04) Crigges: woot? (13:18:06) Cokemonkey11: yeah, okay I'll just do this and stop complaining (13:18:18) Crigges: so because it isn't inside the standard lib (13:18:21) Cokemonkey11: no (13:18:21) Cokemonkey11: no (13:18:24) Cokemonkey11: I will use this (13:18:30) Crigges: okay (13:18:35) Cokemonkey11: the thing that makes me uncomfortable is that (13:18:45) Cokemonkey11: encouraging the use of this will discourage adding it to the compiler (13:19:00) Cokemonkey11: and that means you'll never get compiletime errors for the weird cases we talked about previously (13:19:14) Cokemonkey11: I would prefer to have the same functionality built-in (13:19:24) Crigges: i get compiletime erros (13:19:25) peq: currently you get compiletime errors for all the weird cases we talked about (13:19:36) Crigges: since i throw them myself (13:19:43) Cokemonkey11: do you get compiletime errors for this example? http://peeeq.de/code.php?id=9025 (13:20:06) Cokemonkey11: crigges, you wrote an annotation and made all compiletime code require non-side-effect calls only? (13:20:30) Cokemonkey11: if compiletime is still unnecessary indirection (13:20:42) Crigges: nah not realy (13:20:42) peq: you would get an error in placeItem() (13:20:56) peq: because it would use a runtime function at compiletime (13:21:04) Crigges: yes (13:21:18) Cokemonkey11: okay but change the code to use if compiletime (13:21:25) Cokemonkey11: without changing the behavior of the incrementor (13:21:40) Cokemonkey11: and you have erroneous code without compiletime error (13:21:43) Cokemonkey11: and ugly indirection (13:21:55) Cokemonkey11: and no strategy for resolution in future (13:21:58) peq: but it would be quite obvious that it is wrong (13:22:00) Cokemonkey11: in the general case (13:22:19) Crigges: https://github.com/Crigges/HotN/blob/master/HotN/wurst/UnitStuff/UnitTypes.wurst (13:22:32) Cokemonkey11: i would argue that using @initcompile like I suggest, should also have obvious erroneous code (13:23:33) Crigges: but just for the readability why? (13:23:47) Crigges: it would be like one of this vjass features (13:24:04) Crigges: nobody knows what's the code does exactly (13:24:31) Crigges: take a look @the last function of the UnitTypes package (13:24:47) Cokemonkey11: you can write unintelligible code in most languages (13:24:48) Cokemonkey11: yes I saw that (13:24:52) Cokemonkey11: I see how it works (13:24:53) Crigges: i throw compile errors myself if something is wrong (13:26:07) Crigges: and since the compiletime code is captured inside this class (13:26:19) Crigges: you can't destroy anything from outside (13:26:54) Cokemonkey11: yes but you don't have any strategy for compiletime errors if local variables in the init case differ from the compiletime case (13:27:07) Cokemonkey11: and there's not any reasonable way you can add that (13:27:23) Cokemonkey11: actually there is, but its so complicated (13:27:48) Cokemonkey11: you would need to somehow require that all variables used in the compiletime case were only used by methods marked as not having side-effects (13:27:57) Cokemonkey11: reasonable in a compiler, not in wurst (13:29:08) Cokemonkey11: and writing if/else is almost as ugly as having 2 independent functions (13:29:13) Crigges: i don't need a way for that (13:29:25) Cokemonkey11: I don't need it either, but someone might (13:29:31) Cokemonkey11: okay, I finally go now, be back in a bit (13:29:42) Crigges: wow (13:30:06) Crigges: ^I don't need it either, but someone might and this is why we arguing

Cokemonkey11 commented 9 years ago

Crigges has made a design pattern that a lot of people will use.

It works by injecting a compiletime boolean into the scope of the function, and runs that function twice.

This API is not ideal, and requires careful explanation.

It is still prone to the counterexamples from before.

It has no roadmap for improving compiletime errors, because wurst should not be used for validating side-effects of functions.