Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
calculate_functors [2026/07/29 13:34]
hermann
calculate_functors [2026/07/29 13:42] (current)
hermann
Line 529: Line 529:
 If the computed real value exceeds the range of the chosen Cell Type, that cell is also written as null rather than wrapping or clipping. This range check applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors that accept a Cell Type parameter and write to a typed cell grid. If the computed real value exceeds the range of the chosen Cell Type, that cell is also written as null rather than wrapping or clipping. This range check applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors that accept a Cell Type parameter and write to a typed cell grid.
  
-===== Guarding ​a conditional'​s test against null =====+===== When a conditional'​s test needs guarding ​against null =====
  
-A conditional ​whose test doesn'​t ​explicitly check for null still behaves correctly as long as the same operand being tested ​is also what gets returned. ''​if i1 = 2 then 10 else i1''​ is internally equivalent to ''​if isNull(i1) then null else if i1 = 2 then 10 else i1'' ​— when ''​i1''​ is null, the whole expression is null, which is exactly the desired behaviour when reclassifying a single map in place.+A conditional'​s ​test poisoning the whole result is only a problem when the tested operand'​s nullness shouldn'​t ​determine whether ​the entire expression ​is invalidReclassifying ''​i1''​ in place — ''​if i1 = 2 then 10 else i1'' ​— needs no explicit guard, since a null ''​i1''​ making the reclassified result null for that cell is exactly the desired behaviour: the expression ​is internally equivalent to ''​if isNull(i1) then null else if i1 = 2 then 10 else i1''​.
  
-The same pattern breaks ​when the null being tested ​belongs to a //​different//​ operand than the one being preserved. Combining a landscape map (''​i1''​) with a road map (''​i2'',​ where 1 marks a road and null marks everything else) might suggest ''​if i2 = 1 then 1 else i1''​ — but this is internally equivalent to ''​if isNull(i2) then null else if i2 = 1 then 1 else i1''​. Since most cells are null in a sparse road map, most of the output becomes null too, discarding ''​i1'''​s landscape values everywhere a road isn't present.+The same mechanism causes a real bug when the operand ​being tested ​isn'​t ​the one the null-ness should actually be judged against. Combining a landscape map (''​i1''​) with a road map (''​i2'',​ where 1 marks a road and null marks everything else) might suggest ''​if i2 = 1 then 1 else i1''​ — but this is internally equivalent to ''​if isNull(i2) then null else if i2 = 1 then 1 else i1''​. Since most cells are null in a sparse road map, most of the output becomes null too, discarding ''​i1'''​s landscape values everywhere a road isn't present ​— even though ''​i2''​ being null says nothing about whether ''​i1'''​s value is valid.
  
 Guarding the test with ''​isNull''​ fixes it: ''​if not isNull(i2) and i2 = 1 then 1 else i1''​ only tests ''​i2''​ once it's confirmed non-null, so a null road cell falls through to the landscape value in ''​i1''​ instead of nulling the result. Guarding the test with ''​isNull''​ fixes it: ''​if not isNull(i2) and i2 = 1 then 1 else i1''​ only tests ''​i2''​ once it's confirmed non-null, so a null road cell falls through to the landscape value in ''​i1''​ instead of nulling the result.