Code Positivity: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
(first "commit")
 
m (Fix)
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Writing SQF code without negation ==
{{TOC|side}}
This page provides some insights into what it means to try and write SQF in a positive way. Meaning: in a way that tries to avoid negation as much as is practical.
This page provides some insights into what it means to try and write code in a positive way, with the least negation as possible.


=== What is "negation"? ===
Negation refers to formulating a sentence in a negative way. Here are some examples:
* "do NOT do that!"
* "do NOT change that thing!"
* "he is NOT happy!"
Simply put: any sentence with the word "NOT" in it is using negation.


=== Why care about negation? ===
== Overview ==
Research has shown ( look it up if you wish ) that the human brain has more difficulty processing sentences with negation in it. Which could lead to the need for reading a sentence multiple times.<br>
The same goes for code in any language.


=== [[exitWith]] is your friend :) ===
Code negation is (usually) using [[not]] (in [[SQF Syntax|SQF]]/[[SQS Syntax|SQS]]) or {{hl|!}} (in both [[SQF Syntax|SQF]]/[[SQS Syntax|SQS]] and [[DayZ:Enforce Script Syntax|Enforce]] {{GameCategory|armaR|Modding|Guidelines|Scripting|text= Script}}); all in all, it is about searching for the opposite of what one wants. Examples:
To show you why, here is an example of how something could be written '''with''' negation, followed by code '''without''' negation that does exactly the same thing.
{| class="wikitable valign-top" style="width: 100%"
! style="width: 50%" | SQF
! style="width: 50%" | Enforce Script
|-
|
<sqf>
<sqf>
/*
if (not alive player) then
  with negation, basic checks on params.
{
  a typical "only continue if all parameters meet the required conditions"
hint "you are dead";
*/
}
[ controlNull, objNull, 0, true ] call {
else
  if( !isNull( _this select 0 ) ) then {
{
      if( !isNull( _this select 1 ) ) then {
hint "you are alive";
        if( !isNil{ _this select 2 } ) then {
            if( !isNil{ _this select 3 } ) then {
              systemChat "all checks passed";
            };
        };
      };
  };
};
};
</sqf>
</sqf>
<sqf>
|
// same result as example above, but without negation.
<enforce>
[ controlNull, objNull, 0, true ] call {
if (value < minValue)
  if( isNull( _this select 0 ) ) exitWith {};
{
  if( isNull( _this select 1 ) ) exitWith {};
Print("Value is invalid");
  if( isNil{ _this select 2 } ) exitWith {};
}
  if( isNil{ _this select 3 } ) exitWith {};
else
  systemChat "all checks passed";
{
Print("Value is valid");
}
</enforce>
|}
 
Research<ref>https://www.researchgate.net/publication/10733819_Effects_of_Negation_and_Situational_Presence_on_the_Accessibility_of_Text_Information</ref>
has<ref>https://www.researchgate.net/publication/221438570_Some_Issues_on_Detecting_Negation_from_Text</ref>
shown<ref>https://medium.com/@Cuadraman/why-to-stop-writting-negavite-code-af5ffb17195</ref>
that '''negation''' in general (not just in code but also in reading, teaching etc) makes processing sentences more difficult for the human brain, making its message less impactful
(although not in all cases<ref>https://www.psychologicalscience.org/news/releases/true-or-false-how-our-brain-processes-negative-statements.html</ref>).
 
 
== Examples ==
 
{| class="wikitable valign-top" style="width: 100%"
! style="width: 50%" | {{Color|darkgreen|Do}}
! style="width: 50%" | {{Color|darkred|Don't}}
|-
| <sqf>
// this is called {{Link|https://dev.to/arikaturika/one-concept-a-day-early-return-pattern-in-javascript-3pol|Early Return}} / {{Link|https://szymonkrajewski.pl/why-should-you-return-early/|Bouncer}} pattern
if (isNull _control1) exitWith {};
if (isNull _control2) exitWith {};
if (isNull _control3) exitWith {};
 
// do something
</sqf>
| <sqf>
if (not isNull _control1) then
{
if (not isNull _control2) then
{
if (not isNull _control3) then
{
// finally do something
// <- this is called Hadouken code style as per the {{Link|https://www.reddit.com/r/ProgrammerHumor/comments/27yykv/indent_hadouken/|shape}} drawn by the brackets
};
};
};
};
</sqf>
</sqf>
In the two examples above, avoiding negation makes the code a lot more simple and avoids many nested checks.
|-
| <enforce>
// whenever possible without rewriting everything or doubling the methods, of course
if (unit.IsAlive() && unit.IsInjured())
DoHealThing();
</enforce>
<enforce>
// alternatively
if (unit.IsDead())
DoDeathThing();
else if (unit.IsPerfectHealth())
DoPerfectThing();
else // injured
DoHealThing();
</enforce>
| <enforce>
// sometimes, only negative methods are available
if (!unit.IsPerfectHealth() && !unit.IsDead())
DoHealThing();
</enforce>
|}


=== Where avoiding negation becomes...... awkward ===
 
When trying to avoid negation in all types of situations, it can get awkward inside of a function that COULD have a parameter that needs to be handled IF it is something other than Null.<br>
== Counter Examples ==
As you are probably aware, there is no such thing as a command which checks for the opposite of <sqf inline>isNull</sqf>.
 
For example, in a function where passing a control is '''optional''':
As most if not all rules of development, this rule is a rule of thumb that is '''not''' to be enforced at all cost.
<sqf>
 
[ controlNull, true ] call {
{| class="wikitable valign-top" style="width: 100%"
  params [
! style="width: 50%" | {{Color|darkred|Don't}}
      ["_this0", controlNull,[controlNull]],
! style="width: 50%" | {{Color|darkgreen|Do}}
      ["_this1", false,[false]]
<!-- See what I did here? -->
  ];
|-
  if NOT(isNull _this0) then { systemChat "the control exists" };
| <sqf>
  // if it is null, just move on quietly.
if (isNull _control) then
  if( _this select 1 ) then { systemChat str( random 9000 ) };
{
}
else
{
systemChat "the control exists";
};
 
// or, even more convoluted and less performance-friendly
if (str _control find "NULL" == -1) then
{
systemChat "the control exists";
};
};
</sqf>
</sqf>
In the example above, the goal is to just move on if an argument is missing or null. therefore, avoiding negation with <sqf inline>exitWith</sqf> is impossible.<br>
| <sqf>
If one were to write that same code without negation, '''it would become very complicated, harder to read, and SLOWER''' than simply using <sqf inline>NOT</sqf>.
if (not isNull _control) then // neater
An example of that can be seen below:
{
<sqf>
systemChat "the control exists";
[ player ] call {
  if( ( ( str( _this select 0 ) find "NULL" ) == -1 ) then {}; // yes it works, but is a lot slower than just using NOT(isNull)
};
};
</sqf>  
</sqf>
What that does is it wraps the parameter into a string, which for [[objNull]] would become <sqf inline>"<NULL-object>"</sqf>.<br>
|-
Then, simply look for <sqf inline>"NULL"</sqf> in there, and if [[find]] returns -1, it sure is a valid object!
| <enforce>
if (m_aElements.Count() > 0)
Print("There are elements!");
// note that in some cases, storing the count in a variable
// saves further calculations in below code
</enforce>
| <enforce>
if (!m_aElements.IsEmpty())
Print("There are elements!");
</enforce>
|}


=== CONCLUSION ===
Yes, it is possible to avoid negation entirely. However, there is a cost. '''Depending on the situation, it can lead to a significant increase in code execution time.'''.<br>
Therefore, it would be fair to say that striving to avoid negation can be beneficial to the readability of the code, but it becomes a paradox when avoiding it in any given situation.
<br>
{{ Feature | Informative | This page means to only offer up another way of writing SQF code, without invoking any hierarchical '''this is better''' nonsense.}}


== See also ==
== See Also ==


* [[Code Optimisation]]
* [[Code Optimisation]]

Latest revision as of 18:15, 16 May 2024

This page provides some insights into what it means to try and write code in a positive way, with the least negation as possible.


Overview

Code negation is (usually) using not (in SQF/SQS) or ! (in both SQF/SQS and Enforce Script); all in all, it is about searching for the opposite of what one wants. Examples:

SQF Enforce Script

if (not alive player) then { hint "you are dead"; } else { hint "you are alive"; };

if (value < minValue) { Print("Value is invalid"); } else { Print("Value is valid"); }

Research[1] has[2] shown[3] that negation in general (not just in code but also in reading, teaching etc) makes processing sentences more difficult for the human brain, making its message less impactful (although not in all cases[4]).


Examples

Do Don't
// this is called Early Return / Bouncer pattern if (isNull _control1) exitWith {}; if (isNull _control2) exitWith {}; if (isNull _control3) exitWith {}; // do something
if (not isNull _control1) then { if (not isNull _control2) then { if (not isNull _control3) then { // finally do something // <- this is called Hadouken code style as per the shape drawn by the brackets }; }; };
// whenever possible without rewriting everything or doubling the methods, of course if (unit.IsAlive() && unit.IsInjured()) DoHealThing();

// alternatively if (unit.IsDead()) DoDeathThing(); else if (unit.IsPerfectHealth()) DoPerfectThing(); else // injured DoHealThing();

// sometimes, only negative methods are available if (!unit.IsPerfectHealth() && !unit.IsDead()) DoHealThing();


Counter Examples

As most if not all rules of development, this rule is a rule of thumb that is not to be enforced at all cost.

Don't Do
if (isNull _control) then { } else { systemChat "the control exists"; }; // or, even more convoluted and less performance-friendly if (str _control find "NULL" == -1) then { systemChat "the control exists"; };
if (not isNull _control) then // neater { systemChat "the control exists"; };
if (m_aElements.Count() > 0) Print("There are elements!"); // note that in some cases, storing the count in a variable // saves further calculations in below code
if (!m_aElements.IsEmpty()) Print("There are elements!");


See Also