ID:1391936
 
(See the best response by DarkCampainger.)
I've always tried to use only the most basic built-in procedures.

For example I always used:
- spawn() instead of sleep()
- loops instead of while()
- for loop instead of for list
- istype() instead of its derived procedures
- MouseDown() and MouseUp() instead of Click()
- new datums instead of predefined datums (like /mob)

Also I've never used or at least tried to avoid:
- any built-in movement/combat and other systems
- any random procedures
- preprocessor commands

As long as I don't know much about those not used it'd be silly of me not to ask for their pros and cons.

I'd really like to know whether what am I doing is good or bad for me and my codes.
Best response
The things you have listed are not interchangeable, and you are missing out by not allowing yourself to use both.

I recommend reading the reference to see the differences, but in general:

spawn() is non-blocking, sleep() is blocking

If by "loops" you mean recursion, that can be less efficient than a simple while() loop (and it'll blow up your stack if you don't spawn() it properly)

for() list is powerful in that it can filter the contents by type, and has a special property with break where the iterator (if its scope is greater than the for() loop itself) will only be non-null if break was called (great for searching for a specific item)

ismob()/turf/obj have the handy ability to test multiple arguments

MouseDown() and MouseUp() generate extra communication between the client and server. If you just need to know if they clicked something, Click() requires less network usage (which means your game can support more players)

Not sure what to say about avoiding /atom types. You're basically reinventing the wheel, but since your code is in a VM, it'll be a much slower and bulkier wheel. I guess you could probably get away with it for a text-based game.
Sleep has good uses. You should use it instead of spawn unless you absolutely have to. Spawn has overhead costs.

By loops I assume you mean for() loops. while() has uses that for() cannot replicate. For example if you wanted a loop to run forever would be while(1) vs for(var/i = 1,i,i++). Tthings like for(var/i = i, i <= list.len,i++) vs while(var/i <= list.len) i++, that's just style. You have to use while if you want each loop to be different however, for example if you wanted to sometimes go up or sometimes go down. You CAN do the same with a for() loop but then you have to account for the ++ that's already in the loop and things get confusing.

For loops make the code much, much clearer.
for(var/mob/mobtype/M in moblist)
world << M.var.varofvar

vs

var/mob/mobtype/M
for(var/i = 1, i <= moblist.len, i++)
M = moblist[i]
world << M.var.varofvar


By "istype()'s derived procedures" I assume you mean ismob() isturf() islist() etc. If so that's just a waste of typing time. if(istype(L,/list)) takes longer to type if(islist(L)).

Defining and using MouseDown() and MouseUp() has overheads compared to using Click(). Unless you're using the behavior of the two procs, just use Click(). Now the overhead isn't very big though so if you are using the behavior, that's fine.

You're not using /mob? Seriously? Why not? Why reinvent the wheel when it's already there? Same for the built-in movement, the procs are already there. If you don't like them, find a library that adds a delay or whatever.

Avoiding random numbers is a design idea, not a coding practice. Randomness is a good way to anger players and it's kind of overused in general, but it does have a place. Minor randomness is good for reducing repetitiveness.

Preprocessor commands are amazingly useful to avoid "magic numbers" and to assist when building a bitflag system. For exmaple, which of the following do you prefer?
mob/proc/makedizzy()
flags |= 4

vs

#define STUNNED 4 //Put this in a high-level, early loading dm file

mob/proc/makedizzy()
flags |= STUNNED
I can't explain why but using sleep() is just "unclean", I mean it stops the whole proc tree and I can do it other way so I'm definietely not using it anyway.

However, by loops I mean recursion loops, I think using while() proc may look way more simple than recursing the whole proc and maybe i could start using it.

I'm new to programming and I thought eg ismob(a) calls istype(a,/mob) so when I directly type it I can skip the process and thus save some CPU?

MouseDown() and MouseUp() - I usually do some visualeffects so unless they hurt the game I’m going to keep them.

I don't prefer to use predefined atoms because of those predefined procedures. I don't want them to be called. I want to keep it nice and clean and if they were just delayed I'd have a bad feeling about it. Also more libraries and foreign codes would make more mess in my codes, which lead to abandoning my projects.

I don't define flags this way and I think it makes some extra mess in codes and at the end I will use only one setting so I still don't see any use for it.
sleep() is not at all unclean. It serves its use for quite a number of things. It just may not work as intended some of the time.

To me, spawn() is the more unclean of the two, as it can cause some serious issues when used unscrupulously.

But they both serve a perfectly good purpose.
Could you give an example of unscrupulous use of spawn()?
It's highly situational and too hard to explain.

Suffice it to say, some code has to be processed in a certain order to work, and spawning by zero can make things hard to get happening in the correct order.
This would lock the game up.

while(var/i = 1 <= moblist.len)
spawn(20)
var/mob/M = moblist[i]
M.someproc()
i++


If you've used spawn in a proc once, you shouldn't use it anymore inside that proc. If you want to use spawn in a loop, spawn the entire outside of the loop and use sleep within said loop. You can use set waitfor = 0 at the beginning of a proc to make sleep() act like spawn() in that it'll return priority to its calling proc without spawn()'s hilariously bad overhead.
spawn()'s hilariously bad overhead.

Can I get a reference for that? If spawn() does indeed have significant overhead, it's because it has to manipulate the VM's callstack in such a way that the entire scope of the local portion of the stack has to be extracted and maintained.

If you've used spawn in a proc once, you shouldn't use it anymore inside that proc.

spawn() can be used multiple times in a single proc correctly, as well. If you have some reason to schedule multiple behaviors that are independent of one another in every way, but cause, and they should not wait on one another, it's going to require you to spawn() multiple times in one proc.

So, this rule of thumb is false.
Spawn having high overheads is just a fact like del() being slow and goto being stupid. If you want proof do a test case with 10,000,000 calls of a proc using sleep vs 10,000,000 calls of that same proc with spawn(). My understanding of spawn is that it literally creates a new running proc and inserts said proc into the callstack. There's no need to do that 99% of the time when sleep and waitfor can accomplish the exact same thing.

A rule of thumb doesn't = true 100% of the time. The situation I was assuming was
spawn(10)
Do something
spawn(20)
Do something else
spawn(30)
Do another thing


Which should really be
spawn(0)
sleep(10)
Do something
sleep(10)
Do something else
sleep(10)
Do another thing


I can't imagine somewhere you'd have to spawn() code off multiple times except a loop with a spawn() in it which is bad practice but in rare cases necessary with for instance spawn(-1).
Anything that requires multiple user input.

gm/verb/contest()
var/timeout = 1
var/list/contest = new()
for(var/mob/m in world)
if(m.client)
spawn(0)
if(alert(m,"A new contest is starting, do you want to join?")=="Yes")
if(timeout)
contest += m
m << "You were added to the contest."
sleep(1000)
timeout = null
return contest


Also, the codes you posted are not functionally equivalent, especially for a long running function.
var/list/mob/contestants = list()
var/contest_starting_up //Would be better as a bitflag

mob/proc/ask_to_join_contest()
set waitfor = 0
sleep(0) //Should return focus back to the caller. If not, use some sort of browse()
//system, I'm not writing that. However if you want a quick solution then spawn()
if(alert(src,"A new contest is starting, do you want to join?) == "Yes")
if(contest_starting_up)
contestants += src
src << "You were added to the contest."

gm/verb/contest()
contest_starting_up = 1
contestants = list()
for(var/mob/M in world)
M.ask_to_join_contest()
sleep(1000)
return contestants
sleep does not continue code execution or cause a return, which is the point I am making.

alert locks up the process waiting for an input. Just because something has some side effects doesn't mean you should refuse use it, otherwise we wouldn't be doing anything in any engine.
Sleep does return focus back to its caller if you use set waitfor = 0. Read the red book.
In response to MisterPerson
MisterPerson wrote:
If you want proof do a test case with 10,000,000 calls of a proc using sleep vs 10,000,000 calls of that same proc with spawn().

And... do what, exactly? Figure out how long they take to process? Cause I'd wager the one that uses sleep(1) takes 10 million ticks to process, and the one that takes spawn(1) crashes the program or locks it indefinitely. Because spawning 10 million tasks at once makes no sense, and you should avoid it.

That's not a fault of spawn. That's the fault of someone not understanding how to use it. Just like threading in any other language, spawning 10 million threads at once is not a viable usage.

My understanding of spawn is that it literally creates a new running proc and inserts said proc into the callstack. There's no need to do that 99% of the time when sleep and waitfor can accomplish the exact same thing.

As a rule of thumb, I'd suggest not suggesting people use undocumented features, unless you have a good reason to. Suggesting sleep() and waitfor as an alternative to spawn(), for example, is ridiculous.

A rule of thumb doesn't = true 100% of the time. The situation I was assuming was
spawn(10)
Do something
spawn(20)
Do something else
spawn(30)
Do another thing

Which should really be
 spawn(0)
sleep(10)
Do something
sleep(10)
Do something else
sleep(10)
Do another thing


These aren't the same thing. If someone is using spawn() to replace sleep(), then that is a problem. If someone wants 3 tasks to execute at different times apart from one another starting at the same point that require being separate from each other, then the first case is obviously a valid solution.

As a side note, your example of using set waitfor=0 and sleep(0) are exactly the same as using spawn(). Except using set waitfor=0 and sleep(0) is unintuitive. And as mentioned earlier, undocumented functionality that could stop working on their next update.

TL;DR Use spawn when you need a separate task to execute unattached to the current process. Use sleep when you need a process to halt and continue after a period of time.

The end.